/*
* 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: '
',
smartSelectBackText: 'Back',
smartSelectInPopup: false,
smartSelectPopupCloseTemplate: '',
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 ? '' + params.title + '
' : '';
var textHTML = params.text ? '' + 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 ||
'' +
'
' +
'{{leftTemplate}}' +
'
{{pageTitle}}
' +
'
' +
'
'
);
}
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 : '') +
'
' +
'
' +
'
' +
'
';
// 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
vl.ul = vl.params.ul ? $(vl.params.ul) : vl.listBlock.children('ul');
if (vl.ul.length === 0) {
vl.listBlock.append('');
vl.ul = vl.listBlock.children('ul');
}
// DOM cached items
vl.domCache = {};
vl.displayDomCache = {};
// Temporary DOM Element
vl.tempDomElement = document.createElement('ul');
// Last repain position
vl.lastRepaintY = null;
// Fragment
vl.fragment = document.createDocumentFragment();
// Filter
vl.filterItems = function (indexes, resetScrollTop) {
vl.filteredItems = [];
var firstIndex = indexes[0];
var lastIndex = indexes[indexes.length - 1];
for (var i = 0; i < indexes.length; i++) {
vl.filteredItems.push(vl.items[indexes[i]]);
}
if (typeof resetScrollTop === 'undefined') resetScrollTop = true;
if (resetScrollTop) {
vl.pageContent[0].scrollTop = 0;
}
vl.update();
};
vl.resetFilter = function () {
vl.filteredItems = null;
delete vl.filteredItems;
vl.update();
};
var pageHeight, rowsPerScreen, rowsBefore, rowsAfter, rowsToRender, maxBufferHeight = 0, listHeight;
var dynamicHeight = typeof vl.params.height === 'function';
// Set list size
vl.setListSize = function () {
var items = vl.filteredItems || vl.items;
pageHeight = vl.pageContent[0].offsetHeight;
if (dynamicHeight) {
listHeight = 0;
vl.heights = [];
for (var i = 0; i < items.length; i++) {
var itemHeight = vl.params.height(items[i]);
listHeight += itemHeight;
vl.heights.push(itemHeight);
}
}
else {
listHeight = items.length * vl.params.height / vl.params.cols;
rowsPerScreen = Math.ceil(pageHeight / vl.params.height);
rowsBefore = vl.params.rowsBefore || rowsPerScreen * 2;
rowsAfter = vl.params.rowsAfter || rowsPerScreen;
rowsToRender = (rowsPerScreen + rowsBefore + rowsAfter);
maxBufferHeight = rowsBefore / 2 * vl.params.height;
}
if (updatableScroll) {
vl.ul.css({height: listHeight + 'px'});
}
};
// Render items
vl.render = function (force) {
if (force) vl.lastRepaintY = null;
// var scrollTop = vl.pageContent[0].scrollTop;
var scrollTop = -(vl.listBlock[0].getBoundingClientRect().top + vl.pageContent[0].getBoundingClientRect().top);
if (vl.lastRepaintY === null || Math.abs(scrollTop - vl.lastRepaintY) > maxBufferHeight || (!updatableScroll && (vl.pageContent[0].scrollTop + pageHeight >= vl.pageContent[0].scrollHeight))) {
vl.lastRepaintY = scrollTop;
}
else {
return;
}
var items = vl.filteredItems || vl.items,
fromIndex, toIndex, heightBeforeFirstItem = 0, heightBeforeLastItem = 0;
if (dynamicHeight) {
var itemTop = 0, j, itemHeight;
maxBufferHeight = pageHeight;
for (j = 0; j < vl.heights.length; j++) {
itemHeight = vl.heights[j];
if (typeof fromIndex === 'undefined') {
if (itemTop + itemHeight >= scrollTop - pageHeight * 2) fromIndex = j;
else heightBeforeFirstItem += itemHeight;
}
if (typeof toIndex === 'undefined') {
if (itemTop + itemHeight >= scrollTop + pageHeight * 2 || j === vl.heights.length - 1) toIndex = j + 1;
heightBeforeLastItem += itemHeight;
}
itemTop += itemHeight;
}
toIndex = Math.min(toIndex, items.length);
}
else {
fromIndex = (parseInt(scrollTop / vl.params.height) - rowsBefore) * vl.params.cols;
if (fromIndex < 0) {
fromIndex = 0;
}
toIndex = Math.min(fromIndex + rowsToRender * vl.params.cols, items.length);
}
var topPosition;
vl.reachEnd = false;
for (var i = fromIndex; i < toIndex; i++) {
var item, index;
// Define real item index
index = vl.items.indexOf(items[i]);
if (i === fromIndex) vl.currentFromIndex = index;
if (i === toIndex - 1) vl.currentToIndex = index;
if (index === vl.items.length - 1) vl.reachEnd = true;
// Find items
if (vl.domCache[index]) {
item = vl.domCache[index];
}
else {
if (vl.template) {
vl.tempDomElement.innerHTML = vl.template(items[i], {index: index});
}
else if (vl.params.renderItem) {
vl.tempDomElement.innerHTML = vl.params.renderItem(index, items[i]);
}
else {
vl.tempDomElement.innerHTML = items[i];
}
item = vl.tempDomElement.childNodes[0];
if (vl.params.cache) vl.domCache[index] = item;
}
item.f7VirtualListIndex = index;
// Set item top position
if (i === fromIndex) {
if (dynamicHeight) {
topPosition = heightBeforeFirstItem;
}
else {
topPosition = (i * vl.params.height / vl.params.cols);
}
}
item.style.top = topPosition + 'px';
// Before item insert
if (vl.params.onItemBeforeInsert) vl.params.onItemBeforeInsert(vl, item);
// Append item to fragment
vl.fragment.appendChild(item);
}
// Update list height with not updatable scroll
if (!updatableScroll) {
if (dynamicHeight) {
vl.ul[0].style.height = heightBeforeLastItem + 'px';
}
else {
vl.ul[0].style.height = i * vl.params.height / vl.params.cols + 'px';
}
}
// Update list html
if (vl.params.onBeforeClear) vl.params.onBeforeClear(vl, vl.fragment);
vl.ul[0].innerHTML = '';
if (vl.params.onItemsBeforeInsert) vl.params.onItemsBeforeInsert(vl, vl.fragment);
vl.ul[0].appendChild(vl.fragment);
if (vl.params.onItemsAfterInsert) vl.params.onFragmentAfterInsert(vl, vl.fragment);
};
// Handle scroll event
vl.handleScroll = function (e) {
vl.render();
};
// Handle resize event
vl.handleResize = function (e) {
vl.setListSize();
vl.render(true);
};
vl.attachEvents = function (detach) {
var action = detach ? 'off' : 'on';
vl.pageContent[action]('scroll', vl.handleScroll);
$(window)[action]('resize', vl.handleResize);
};
// Init Virtual List
vl.init = function () {
vl.attachEvents();
vl.setListSize();
vl.render();
};
// Append
vl.appendItems = function (items) {
for (var i = 0; i < items.length; i++) {
vl.items.push(items[i]);
}
vl.update();
};
vl.appendItem = function (item) {
vl.appendItems([item]);
};
// Replace
vl.replaceAllItems = function (items) {
vl.items = items;
delete vl.filteredItems;
vl.domCache = {};
vl.update();
};
vl.replaceItem = function (index, item) {
vl.items[index] = item;
if (vl.params.cache) delete vl.domCache[index];
vl.update();
};
// Prepend
vl.prependItems = function (items) {
for (var i = items.length - 1; i >= 0; i--) {
vl.items.unshift(items[i]);
}
if (vl.params.cache) {
var newCache = {};
for (var cached in vl.domCache) {
newCache[parseInt(cached, 10) + items.length] = vl.domCache[cached];
}
vl.domCache = newCache;
}
vl.update();
};
vl.prependItem = function (item) {
vl.prependItems([item]);
};
// Move
vl.moveItem = function (oldIndex, newIndex) {
if (oldIndex === newIndex) return;
// remove item from array
var item = vl.items.splice(oldIndex, 1)[0];
if (newIndex >= vl.items.length) {
// Add item to the end
vl.items.push(item);
newIndex = vl.items.length - 1;
}
else {
// Add item to new index
vl.items.splice(newIndex, 0, item);
}
// Update cache
if (vl.params.cache) {
var newCache = {};
for (var cached in vl.domCache) {
var cachedIndex = parseInt(cached, 10);
var leftIndex = oldIndex < newIndex ? oldIndex : newIndex;
var rightIndex = oldIndex < newIndex ? newIndex : oldIndex;
var indexShift = oldIndex < newIndex ? -1 : 1;
if (cachedIndex < leftIndex || cachedIndex > rightIndex) newCache[cachedIndex] = vl.domCache[cachedIndex];
if (cachedIndex === leftIndex) newCache[rightIndex] = vl.domCache[cachedIndex];
if (cachedIndex > leftIndex && cachedIndex <= rightIndex) newCache[cachedIndex + indexShift] = vl.domCache[cachedIndex];
}
vl.domCache = newCache;
}
vl.update();
};
// Insert before
vl.insertItemBefore = function (index, item) {
if (index === 0) {
vl.prependItem(item);
return;
}
if (index >= vl.items.length) {
vl.appendItem(item);
return;
}
vl.items.splice(index, 0, item);
// Update cache
if (vl.params.cache) {
var newCache = {};
for (var cached in vl.domCache) {
var cachedIndex = parseInt(cached, 10);
if (cachedIndex >= index) {
newCache[cachedIndex + 1] = vl.domCache[cachedIndex];
}
}
vl.domCache = newCache;
}
vl.update();
};
// Delete
vl.deleteItems = function (indexes) {
var prevIndex, indexShift = 0;
for (var i = 0; i < indexes.length; i++) {
var index = indexes[i];
if (typeof prevIndex !== 'undefined') {
if (index > prevIndex) {
indexShift = -i;
}
}
index = index + indexShift;
prevIndex = indexes[i];
// Delete item
var deletedItem = vl.items.splice(index, 1)[0];
// Delete from filtered
if (vl.filteredItems && vl.filteredItems.indexOf(deletedItem) >= 0) {
vl.filteredItems.splice(vl.filteredItems.indexOf(deletedItem), 1);
}
// Update cache
if (vl.params.cache) {
var newCache = {};
for (var cached in vl.domCache) {
var cachedIndex = parseInt(cached, 10);
if (cachedIndex === index) {
delete vl.domCache[index];
}
else if (parseInt(cached, 10) > index) {
newCache[cachedIndex - 1] = vl.domCache[cached];
}
else {
newCache[cachedIndex] = vl.domCache[cached];
}
}
vl.domCache = newCache;
}
}
vl.update();
};
vl.deleteAllItems = function () {
vl.items = [];
delete vl.filteredItems;
if (vl.params.cache) vl.domCache = {};
vl.update();
};
vl.deleteItem = function (index) {
vl.deleteItems([index]);
};
// Clear cache
vl.clearCache = function () {
vl.domCache = {};
};
// Update Virtual List
vl.update = function () {
vl.setListSize();
vl.render(true);
};
// Destroy
vl.destroy = function () {
vl.attachEvents(true);
delete vl.items;
delete vl.domCache;
};
// Init Virtual List
vl.init();
// Store vl in container
vl.listBlock[0].f7VirtualList = vl;
return vl;
};
// App Method
app.virtualList = function (listBlock, params) {
return new VirtualList(listBlock, params);
};
app.reinitVirtualList = function (pageContainer) {
var page = $(pageContainer);
var vlists = page.find('.virtual-list');
if (vlists.length === 0) return;
for (var i = 0; i < vlists.length; i++) {
var vlistInstance = vlistInstance[0].f7VirtualList;
if (vlistInstance) {
vlistInstance.update();
}
}
};
/*======================================================
************ Pull To Refresh ************
======================================================*/
app.initPullToRefresh = function (pageContainer) {
var eventsTarget = $(pageContainer);
if (!eventsTarget.hasClass('pull-to-refresh-content')) {
eventsTarget = eventsTarget.find('.pull-to-refresh-content');
}
if (!eventsTarget || eventsTarget.length === 0) return;
var isTouched, isMoved, touchesStart = {}, isScrolling, touchesDiff, touchStartTime, container, refresh = false, useTranslate = false, startTranslate = 0, translate, scrollTop, wasScrolled, layer;
var page = eventsTarget.hasClass('page') ? eventsTarget : eventsTarget.parents('.page');
var hasNavbar = false;
if (page.find('.navbar').length > 0 || page.parents('.navbar-fixed, .navbar-through').length > 0 || page.hasClass('navbar-fixed') || page.hasClass('navbar-through')) hasNavbar = true;
if (page.hasClass('no-navbar')) hasNavbar = false;
if (!hasNavbar) eventsTarget.addClass('pull-to-refresh-no-navbar');
container = eventsTarget;
function handleTouchStart(e) {
if (isTouched) {
if (app.device.os === 'android') {
if ('targetTouches' in e && e.targetTouches.length > 1) return;
}
else return;
}
isMoved = false;
isTouched = true;
isScrolling = undefined;
wasScrolled = 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();
/*jshint validthis:true */
container = $(this);
}
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;
}
scrollTop = container[0].scrollTop;
if (typeof wasScrolled === 'undefined' && scrollTop !== 0) wasScrolled = true;
if (!isMoved) {
/*jshint validthis:true */
container.removeClass('transitioning');
// layer.removeClass('transitioning');
if (scrollTop > container[0].offsetHeight) {
isTouched = false;
return;
}
startTranslate = container.hasClass('refreshing') ? 44 : 0;
if (container[0].scrollHeight === container[0].offsetHeight || app.device.os !== 'ios') {
useTranslate = true;
}
else {
useTranslate = false;
}
}
isMoved = true;
touchesDiff = pageY - touchesStart.y;
if (touchesDiff > 0 && scrollTop <= 0 || scrollTop < 0) {
// iOS 8 fix
if (app.device.os === 'ios' && parseInt(app.device.osVersion.split('.')[0], 10) > 7 && scrollTop === 0 && !wasScrolled) useTranslate = true;
if (useTranslate) {
e.preventDefault();
translate = (Math.pow(touchesDiff, 0.85) + startTranslate);
container.transform('translate3d(0,' + translate + 'px,0)');
}
else {
}
if ((useTranslate && Math.pow(touchesDiff, 0.85) > 44) || (!useTranslate && touchesDiff >= 88)) {
refresh = true;
container.addClass('pull-up').removeClass('pull-down');
}
else {
refresh = false;
container.removeClass('pull-up').addClass('pull-down');
}
}
else {
container.removeClass('pull-up pull-down');
refresh = false;
return;
}
}
function handleTouchEnd(e) {
if (!isTouched || !isMoved) {
isTouched = false;
isMoved = false;
return;
}
if (translate) {
container.addClass('transitioning');
translate = 0;
}
container.transform('');
if (refresh) {
container.addClass('refreshing');
container.trigger('refresh', {
done: function () {
app.pullToRefreshDone(container);
}
});
}
else {
container.removeClass('pull-down');
}
isTouched = false;
isMoved = false;
}
// Attach Events
eventsTarget.on(app.touchEvents.start, handleTouchStart);
eventsTarget.on(app.touchEvents.move, handleTouchMove);
eventsTarget.on(app.touchEvents.end, handleTouchEnd);
// Detach Events on page remove
if (page.length === 0) return;
function destroyPullToRefresh() {
eventsTarget.off(app.touchEvents.start, handleTouchStart);
eventsTarget.off(app.touchEvents.move, handleTouchMove);
eventsTarget.off(app.touchEvents.end, handleTouchEnd);
}
eventsTarget[0].f7DestroyPullToRefresh = destroyPullToRefresh;
function detachEvents() {
destroyPullToRefresh();
page.off('pageBeforeRemove', detachEvents);
}
page.on('pageBeforeRemove', detachEvents);
};
app.pullToRefreshDone = function (container) {
container = $(container);
if (container.length === 0) container = $('.pull-to-refresh-content.refreshing');
container.removeClass('refreshing').addClass('transitioning');
container.transitionEnd(function () {
container.removeClass('transitioning pull-up pull-down');
});
};
app.pullToRefreshTrigger = function (container) {
container = $(container);
if (container.length === 0) container = $('.pull-to-refresh-content');
if (container.hasClass('refreshing')) return;
container.addClass('transitioning refreshing');
container.trigger('refresh', {
done: function () {
app.pullToRefreshDone(container);
}
});
};
app.destroyPullToRefresh = function (pageContainer) {
pageContainer = $(pageContainer);
var pullToRefreshContent = pageContainer.hasClass('pull-to-refresh-content') ? pageContainer : pageContainer.find('.pull-to-refresh-content');
if (pullToRefreshContent.length === 0) return;
if (pullToRefreshContent[0].f7DestroyPullToRefresh) pullToRefreshContent[0].f7DestroyPullToRefresh();
};
/* ===============================================================================
************ Infinite Scroll ************
=============================================================================== */
function handleInfiniteScroll() {
/*jshint validthis:true */
var inf = $(this);
var scrollTop = inf[0].scrollTop;
var scrollHeight = inf[0].scrollHeight;
var height = inf[0].offsetHeight;
var distance = inf[0].getAttribute('data-distance');
var virtualListContainer = inf.find('.virtual-list');
var virtualList;
if (!distance) distance = 50;
if (typeof distance === 'string' && distance.indexOf('%') >= 0) {
distance = parseInt(distance, 10) / 100 * height;
}
if (distance > height) distance = height;
if (scrollTop + height >= scrollHeight - distance) {
if (virtualListContainer.length > 0) {
virtualList = virtualListContainer[0].f7VirtualList;
if (virtualList && !virtualList.reachEnd) return;
}
inf.trigger('infinite');
}
}
app.attachInfiniteScroll = function (infiniteContent) {
$(infiniteContent).on('scroll', handleInfiniteScroll);
};
app.detachInfiniteScroll = function (infiniteContent) {
$(infiniteContent).off('scroll', handleInfiniteScroll);
};
app.initInfiniteScroll = function (pageContainer) {
pageContainer = $(pageContainer);
var infiniteContent = pageContainer.find('.infinite-scroll');
if (infiniteContent.length === 0) return;
app.attachInfiniteScroll(infiniteContent);
function detachEvents() {
app.detachInfiniteScroll(infiniteContent);
pageContainer.off('pageBeforeRemove', detachEvents);
}
pageContainer.on('pageBeforeRemove', detachEvents);
};
/*=============================================================
************ Hide/show Toolbar/Navbar on scroll ************
=============================================================*/
app.initScrollToolbars = function (pageContainer) {
pageContainer = $(pageContainer);
var scrollContent = pageContainer.find('.page-content');
if (scrollContent.length === 0) return;
var hideNavbar = app.params.hideNavbarOnPageScroll || scrollContent.hasClass('hide-navbar-on-scroll') || scrollContent.hasClass('hide-bars-on-scroll');
var hideToolbar = app.params.hideToolbarOnPageScroll || scrollContent.hasClass('hide-toolbar-on-scroll') || scrollContent.hasClass('hide-bars-on-scroll');
var hideTabbar = app.params.hideTabbarOnPageScroll || scrollContent.hasClass('hide-tabbar-on-scroll');
if (!(hideNavbar || hideToolbar || hideTabbar)) return;
var viewContainer = scrollContent.parents('.' + app.params.viewClass);
if (viewContainer.length === 0) return;
var navbar = viewContainer.find('.navbar'),
toolbar = viewContainer.find('.toolbar'),
tabbar;
if (hideTabbar) {
tabbar = viewContainer.find('.tabbar');
if (tabbar.length === 0) tabbar = viewContainer.parents('.' + app.params.viewsClass).find('.tabbar');
}
var hasNavbar = navbar.length > 0,
hasToolbar = toolbar.length > 0,
hasTabbar = tabbar && tabbar.length > 0;
var previousScroll, currentScroll;
previousScroll = currentScroll = scrollContent[0].scrollTop;
var scrollHeight, offsetHeight, reachEnd, action, navbarHidden, toolbarHidden, tabbarHidden;
var toolbarHeight = (hasToolbar && hideToolbar) ? toolbar[0].offsetHeight : 0;
var tabbarHeight = (hasTabbar && hideTabbar) ? tabbar[0].offsetHeight : 0;
var bottomBarHeight = tabbarHeight || toolbarHeight;
function handleScroll(e) {
if (pageContainer.hasClass('page-on-left')) return;
currentScroll = scrollContent[0].scrollTop;
scrollHeight = scrollContent[0].scrollHeight;
offsetHeight = scrollContent[0].offsetHeight;
reachEnd = app.params.showBarsOnPageScrollEnd && (currentScroll + offsetHeight >= scrollHeight - bottomBarHeight);
navbarHidden = navbar.hasClass('navbar-hidden');
toolbarHidden = toolbar.hasClass('toolbar-hidden');
tabbarHidden = tabbar && tabbar.hasClass('toolbar-hidden');
if (previousScroll > currentScroll || reachEnd) {
action = 'show';
}
else {
if (currentScroll > 44) {
action = 'hide';
}
else {
action = 'show';
}
}
if (action === 'show') {
if (hasNavbar && hideNavbar && navbarHidden) {
app.showNavbar(navbar);
pageContainer.removeClass('no-navbar-by-scroll');
navbarHidden = false;
}
if (hasToolbar && hideToolbar && toolbarHidden) {
app.showToolbar(toolbar);
pageContainer.removeClass('no-toolbar-by-scroll');
toolbarHidden = false;
}
if (hasTabbar && hideTabbar && tabbarHidden) {
app.showToolbar(tabbar);
pageContainer.removeClass('no-tabbar-by-scroll');
tabbarHidden = false;
}
}
else {
if (hasNavbar && hideNavbar && !navbarHidden) {
app.hideNavbar(navbar);
pageContainer.addClass('no-navbar-by-scroll');
navbarHidden = true;
}
if (hasToolbar && hideToolbar && !toolbarHidden) {
app.hideToolbar(toolbar);
pageContainer.addClass('no-toolbar-by-scroll');
toolbarHidden = true;
}
if (hasTabbar && hideTabbar && !tabbarHidden) {
app.hideToolbar(tabbar);
pageContainer.addClass('no-tabbar-by-scroll');
tabbarHidden = true;
}
}
previousScroll = currentScroll;
}
scrollContent.on('scroll', handleScroll);
scrollContent[0].f7ScrollToolbarsHandler = handleScroll;
};
app.destroyScrollToolbars = function (pageContainer) {
pageContainer = $(pageContainer);
var scrollContent = pageContainer.find('.page-content');
if (scrollContent.length === 0) return;
var handler = scrollContent[0].f7ScrollToolbarsHandler;
if (!handler) return;
scrollContent.off('scroll', scrollContent[0].f7ScrollToolbarsHandler);
};
/* ===============================================================================
************ Tabs ************
=============================================================================== */
app.showTab = function (tab, tabLink) {
var newTab = $(tab);
if (newTab.hasClass('active')) return false;
if (newTab.length === 0) return false;
var tabs = newTab.parent('.tabs');
if (tabs.length === 0) return false;
// Return swipeouts in hidden tabs
app.allowSwipeout = true;
// Animated tabs
var isAnimatedTabs = tabs.parent().hasClass('tabs-animated-wrap');
if (isAnimatedTabs) {
tabs.transform('translate3d(' + -newTab.index() * 100 + '%,0,0)');
}
// Remove active class from old tabs
var oldTab = tabs.children('.tab.active').removeClass('active');
// Add active class to new tab
newTab.addClass('active');
// Trigger 'show' event on new tab
newTab.trigger('show');
// Update navbars in new tab
if (!isAnimatedTabs && newTab.find('.navbar').length > 0) {
// Find tab's view
var viewContainer;
if (newTab.hasClass(app.params.viewClass)) viewContainer = newTab[0];
else viewContainer = newTab.parents('.' + app.params.viewClass)[0];
app.sizeNavbars(viewContainer);
}
// Find related link for new tab
if (tabLink) tabLink = $(tabLink);
else {
// Search by id
if (typeof tab === 'string') tabLink = $('.tab-link[href="' + tab + '"]');
else tabLink = $('.tab-link[href="#' + newTab.attr('id') + '"]');
// Search by data-tab
if (!tabLink || tabLink && tabLink.length === 0) {
$('[data-tab]').each(function () {
if (newTab.is($(this).attr('data-tab'))) tabLink = $(this);
});
}
}
if (tabLink.length === 0) return;
// Find related link for old tab
var oldTabLink;
if (oldTab && oldTab.length > 0) {
// Search by id
var oldTabId = oldTab.attr('id');
if (oldTabId) oldTabLink = $('.tab-link[href="#' + oldTabId + '"]');
// Search by data-tab
if (!oldTabLink || oldTabLink && oldTabLink.length === 0) {
$('[data-tab]').each(function () {
if (oldTab.is($(this).attr('data-tab'))) oldTabLink = $(this);
});
}
}
// Update links' classes
if (tabLink && tabLink.length > 0) tabLink.addClass('active');
if (oldTabLink && oldTabLink.length > 0) oldTabLink.removeClass('active');
return true;
};
/*===============================================================================
************ Accordion ************
===============================================================================*/
app.accordionToggle = function (item) {
item = $(item);
if (item.length === 0) return;
if (item.hasClass('accordion-item-expanded')) app.accordionClose(item);
else app.accordionOpen(item);
};
app.accordionOpen = function (item) {
item = $(item);
var list = item.parents('.accordion-list').eq(0);
var content = item.children('.accordion-item-content');
if (content.length === 0) content = item.find('.accordion-item-content');
var expandedItem = list.length > 0 && item.parent().children('.accordion-item-expanded');
if (expandedItem.length > 0) {
app.accordionClose(expandedItem);
}
content.css('height', content[0].scrollHeight + 'px').transitionEnd(function () {
if (item.hasClass('accordion-item-expanded')) {
content.transition(0);
content.css('height', 'auto');
var clientLeft = content[0].clientLeft;
content.transition('');
item.trigger('opened');
}
else {
content.css('height', '');
item.trigger('closed');
}
});
item.trigger('open');
item.addClass('accordion-item-expanded');
};
app.accordionClose = function (item) {
item = $(item);
var content = item.children('.accordion-item-content');
if (content.length === 0) content = item.find('.accordion-item-content');
item.removeClass('accordion-item-expanded');
content.transition(0);
content.css('height', content[0].scrollHeight + 'px');
// Relayout
var clientLeft = content[0].clientLeft;
// Close
content.transition('');
content.css('height', '').transitionEnd(function () {
if (item.hasClass('accordion-item-expanded')) {
content.transition(0);
content.css('height', 'auto');
var clientLeft = content[0].clientLeft;
content.transition('');
item.trigger('opened');
}
else {
content.css('height', '');
item.trigger('closed');
}
});
item.trigger('close');
};
/*===============================================================================
************ Fast Clicks ************
************ Inspired by https://github.com/ftlabs/fastclick ************
===============================================================================*/
app.initFastClicks = function () {
if (app.params.activeState) {
$('html').addClass('watch-active-state');
}
var touchStartX, touchStartY, touchStartTime, targetElement, trackClick, activeSelection, scrollParent, lastClickTime, isMoved;
var activableElement, activeTimeout, needsFastClick;
function findActivableElement(e) {
var target = $(e.target);
var parents = target.parents(app.params.activeStateElements);
return (parents.length > 0) ? parents : target;
}
function isInsideScrollableView() {
var pageContent = activableElement.parents('.page-content, .panel');
if (pageContent.length === 0) {
return false;
}
// This event handler covers the "tap to stop scrolling".
if (pageContent.prop('scrollHandlerSet') !== 'yes') {
pageContent.on('scroll', function() {
clearTimeout(activeTimeout);
});
pageContent.prop('scrollHandlerSet', 'yes');
}
return true;
}
function addActive() {
activableElement.addClass('active-state');
}
function removeActive(el) {
activableElement.removeClass('active-state');
}
function androidNeedsBlur(el) {
var noBlur = ('button checkbox file image radio submit input textarea').split(' ');
if (document.activeElement && el !== document.activeElement && document.activeElement !== document.body) {
if (noBlur.indexOf(el.nodeName.toLowerCase()) >= 0) {
return false;
}
else {
return true;
}
}
else {
return false;
}
}
function targetNeedsFastClick(el) {
var $el = $(el);
if (el.nodeName.toLowerCase() === 'input' && el.type === 'file') return false;
if ($el.hasClass('no-fastclick') || $el.parents('.no-fastclick').length > 0) return false;
return true;
}
function targetNeedsFocus(el) {
if (document.activeElement === el) {
return false;
}
var tag = el.nodeName.toLowerCase();
var skipInputs = ('button checkbox file image radio submit').split(' ');
if (el.disabled || el.readOnly) return false;
if (tag === 'textarea') return true;
if (tag === 'select') {
if (app.device.os === 'android') return false;
else return true;
}
if (tag === 'input' && skipInputs.indexOf(el.type) < 0) return true;
}
function targetNeedsPrevent(el) {
el = $(el);
if (el.is('label') || el.parents('label').length > 0) {
if (app.device.os === 'android') {
var osv = app.device.osVersion.split('.');
if (osv[0] * 1 > 4 || (osv[0] * 1 === 4 && osv[1] * 1 >= 4)) {
return false;
}
else return true;
}
else return false;
}
return true;
}
// Mouse Handlers
function handleMouseDown (e) {
findActivableElement(e).addClass('active-state');
if ('which' in e && e.which === 3) {
setTimeout(function () {
$('.active-state').removeClass('active-state');
}, 0);
}
}
function handleMouseMove (e) {
$('.active-state').removeClass('active-state');
}
function handleMouseUp (e) {
$('.active-state').removeClass('active-state');
}
// Touch Handlers
function handleTouchStart(e) {
isMoved = false;
if (e.targetTouches.length > 1) {
return true;
}
needsFastClick = targetNeedsFastClick(e.target);
if (!needsFastClick) {
trackClick = false;
return true;
}
if (app.device.os === 'ios') {
var selection = window.getSelection();
if (selection.rangeCount && selection.focusNode !== document.body && (!selection.isCollapsed || document.activeElement === selection.focusNode)) {
activeSelection = true;
return true;
}
else {
activeSelection = false;
}
}
if (app.device.os === 'android') {
if (androidNeedsBlur(e.target)) {
document.activeElement.blur();
}
}
trackClick = true;
targetElement = e.target;
touchStartTime = (new Date()).getTime();
touchStartX = e.targetTouches[0].pageX;
touchStartY = e.targetTouches[0].pageY;
// Detect scroll parent
if (app.device.os === 'ios') {
scrollParent = undefined;
$(targetElement).parents().each(function () {
var parent = this;
if (parent.scrollHeight > parent.offsetHeight && !scrollParent) {
scrollParent = parent;
scrollParent.f7ScrollTop = scrollParent.scrollTop;
}
});
}
if ((e.timeStamp - lastClickTime) < 200) {
e.preventDefault();
}
if (app.params.activeState) {
activableElement = findActivableElement(e);
// If it's inside a scrollable view, we don't trigger active-state yet,
// because it can be a scroll instead. Based on the link:
// http://labnote.beedesk.com/click-scroll-and-pseudo-active-on-mobile-webk
if (!isInsideScrollableView(e)) {
addActive();
} else {
activeTimeout = setTimeout(addActive, 80);
}
}
}
function handleTouchMove(e) {
if (!trackClick) return;
var _isMoved = false;
var distance = app.params.fastClicksDistanceThreshold;
if (distance) {
var pageX = e.targetTouches[0].pageX;
var pageY = e.targetTouches[0].pageY;
if (Math.abs(pageX - touchStartX) > distance || Math.abs(pageY - touchStartY) > distance) {
_isMoved = true;
}
}
else {
_isMoved = true;
}
if (_isMoved) {
trackClick = false;
targetElement = null;
isMoved = true;
}
if (app.params.activeState) {
clearTimeout(activeTimeout);
removeActive();
}
}
function handleTouchEnd(e) {
clearTimeout(activeTimeout);
if (!trackClick) {
if (!activeSelection && needsFastClick) e.preventDefault();
return true;
}
if (document.activeElement === e.target) {
return true;
}
if (!activeSelection) {
e.preventDefault();
}
if ((e.timeStamp - lastClickTime) < 200) {
return true;
}
lastClickTime = e.timeStamp;
trackClick = false;
if (app.device.os === 'ios' && scrollParent) {
if (scrollParent.scrollTop !== scrollParent.f7ScrollTop) {
return false;
}
}
// Add active-state here because, in a very fast tap, the timeout didn't
// have the chance to execute. Removing active-state in a timeout gives
// the chance to the animation execute.
if (app.params.activeState) {
addActive();
setTimeout(removeActive, 0);
}
// Trigger focus when required
if (targetNeedsFocus(targetElement)) {
targetElement.focus();
}
e.preventDefault();
var touch = e.changedTouches[0];
var evt = document.createEvent('MouseEvents');
var eventType = 'click';
if (app.device.os === 'android' && targetElement.nodeName.toLowerCase() === 'select') {
eventType = 'mousedown';
}
evt.initMouseEvent(eventType, true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
evt.forwardedTouchEvent = true;
targetElement.dispatchEvent(evt);
return false;
}
function handleTouchCancel(e) {
trackClick = false;
targetElement = null;
}
function handleClick(e) {
var allowClick = false;
if (trackClick) {
targetElement = null;
trackClick = false;
return true;
}
if (e.target.type === 'submit' && e.detail === 0) {
return true;
}
if (!targetElement) {
allowClick = true;
}
if (document.activeElement === targetElement) {
allowClick = true;
}
if (e.forwardedTouchEvent) {
allowClick = true;
}
if (!e.cancelable) {
allowClick = true;
}
if (!allowClick) {
e.stopImmediatePropagation();
e.stopPropagation();
if (targetElement) {
if (targetNeedsPrevent(targetElement) || isMoved) {
e.preventDefault();
}
}
else {
e.preventDefault();
}
targetElement = null;
}
return allowClick;
}
if (app.support.touch) {
document.addEventListener('click', handleClick, true);
document.addEventListener('touchstart', handleTouchStart);
document.addEventListener('touchmove', handleTouchMove);
document.addEventListener('touchend', handleTouchEnd);
document.addEventListener('touchcancel', handleTouchCancel);
}
else {
if (app.params.activeState) {
document.addEventListener('mousedown', handleMouseDown);
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
}
}
};
/*===============================================================================
************ Handle clicks and make them fast (on tap); ************
===============================================================================*/
app.initClickEvents = function () {
function handleClicks(e) {
/*jshint validthis:true */
var clicked = $(this);
var url = clicked.attr('href');
var isLink = clicked[0].nodeName.toLowerCase() === 'a';
// Str to boolean for data attributes
function toBoolean(str) {
if (str === 'false') return false;
if (str === 'true') return true;
return undefined;
}
// Check if link is external
if (isLink) {
if (clicked.is(app.params.externalLinks)) return;
}
// Smart Select
if (clicked.hasClass('smart-select')) {
if (app.smartSelectOpen) app.smartSelectOpen(clicked);
}
// Open Panel
if (clicked.hasClass('open-panel')) {
if ($('.panel').length === 1) {
if ($('.panel').hasClass('panel-left')) app.openPanel('left');
else app.openPanel('right');
}
else {
if (clicked.attr('data-panel') === 'right') app.openPanel('right');
else app.openPanel('left');
}
}
// Close Panel
if (clicked.hasClass('close-panel')) {
app.closePanel();
}
if (clicked.hasClass('panel-overlay') && app.params.panelsCloseByOutside) {
app.closePanel();
}
// Popover
if (clicked.hasClass('open-popover')) {
var popover;
if (clicked.attr('data-popover')) {
popover = clicked.attr('data-popover');
}
else popover = '.popover';
app.popover(popover, clicked);
}
if (clicked.hasClass('close-popover')) {
app.closeModal('.popover.modal-in');
}
// Popup
var popup;
if (clicked.hasClass('open-popup')) {
if (clicked.attr('data-popup')) {
popup = clicked.attr('data-popup');
}
else popup = '.popup';
app.popup(popup);
}
if (clicked.hasClass('close-popup')) {
if (clicked.attr('data-popup')) {
popup = clicked.attr('data-popup');
}
else popup = '.popup.modal-in';
app.closeModal(popup);
}
// Login Screen
var loginScreen;
if (clicked.hasClass('open-login-screen')) {
if (clicked.attr('data-login-screen')) {
loginScreen = clicked.attr('data-login-screen');
}
else loginScreen = '.login-screen';
app.loginScreen(loginScreen);
}
if (clicked.hasClass('close-login-screen')) {
app.closeModal('.login-screen.modal-in');
}
// Close Modal
if (clicked.hasClass('modal-overlay')) {
if ($('.modal.modal-in').length > 0 && app.params.modalCloseByOutside)
app.closeModal('.modal.modal-in');
if ($('.actions-modal.modal-in').length > 0 && app.params.actionsCloseByOutside)
app.closeModal('.actions-modal.modal-in');
if ($('.popover.modal-in').length > 0) app.closeModal('.popover.modal-in');
}
if (clicked.hasClass('popup-overlay')) {
if ($('.popup.modal-in').length > 0 && app.params.popupCloseByOutside)
app.closeModal('.popup.modal-in');
}
// Tabs
var isTabLink;
if (clicked.hasClass('tab-link')) {
isTabLink = true;
app.showTab(clicked.attr('data-tab') || clicked.attr('href'), clicked);
}
// Swipeout Delete
if (clicked.hasClass('swipeout-delete')) {
if (clicked.attr('data-confirm')) {
var text = clicked.attr('data-confirm');
var title = clicked.attr('data-confirm-title');
if (title) {
app.confirm(text, title, function () {
app.swipeoutDelete(clicked.parents('.swipeout'));
});
}
else {
app.confirm(text, function () {
app.swipeoutDelete(clicked.parents('.swipeout'));
});
}
}
else {
app.swipeoutDelete(clicked.parents('.swipeout'));
}
}
// Sortable
if (clicked.hasClass('toggle-sortable')) {
app.sortableToggle(clicked.data('sortable'));
}
if (clicked.hasClass('open-sortable')) {
app.sortableOpen(clicked.data('sortable'));
}
if (clicked.hasClass('close-sortable')) {
app.sortableClose(clicked.data('sortable'));
}
// Accordion
if (clicked.hasClass('accordion-item-toggle') || (clicked.hasClass('item-link') && clicked.parent().hasClass('accordion-item'))) {
var accordionItem = clicked.parent('.accordion-item');
if (accordionItem.length === 0) accordionItem = clicked.parents('.accordion-item');
if (accordionItem.length === 0) accordionItem = clicked.parents('li');
app.accordionToggle(accordionItem);
}
// Load Page
if (app.params.ajaxLinks && !clicked.is(app.params.ajaxLinks) || !isLink || !app.params.router) {
return;
}
if (isLink) {
e.preventDefault();
}
var validUrl = url && url.length > 0 && url !== '#' && !isTabLink;
var template = clicked.attr('data-template');
if (validUrl || clicked.hasClass('back') || template) {
var view;
if (clicked.attr('data-view')) {
view = $(clicked.attr('data-view'))[0].f7View;
}
else {
view = clicked.parents('.' + app.params.viewClass)[0] && clicked.parents('.' + app.params.viewClass)[0].f7View;
if (view && view.params.linksView) {
if (typeof view.params.linksView === 'string') view = $(view.params.linksView)[0].f7View;
else if (view.params.linksView instanceof View) view = view.params.linksView;
}
}
if (!view) {
if (app.mainView) view = app.mainView;
}
if (!view) return;
var pageName;
if (!template) {
if (url.indexOf('#') === 0 && url !== '#') {
if (view.params.domCache) {
pageName = url.split('#')[1];
url = undefined;
}
else return;
}
if (url === '#' && !clicked.hasClass('back')) return;
}
else {
url = undefined;
}
var animatePages;
if (clicked.attr('data-animatePages')) {
animatePages = toBoolean(clicked.attr('data-animatePages'));
}
else {
if (clicked.hasClass('with-animation')) animatePages = true;
if (clicked.hasClass('no-animation')) animatePages = false;
}
var options = {
animatePages: animatePages,
ignoreCache: toBoolean(clicked.attr('data-ignoreCache')),
force: toBoolean(clicked.attr('data-force')),
reload: toBoolean(clicked.attr('data-reload')),
reloadPrevious: toBoolean(clicked.attr('data-reloadPrevious')),
pageName: pageName,
url: url
};
if (app.params.template7Pages) {
options.contextName = clicked.attr('data-contextName');
var context = clicked.attr('data-context');
if (context) {
options.context = JSON.parse(context);
}
}
if (template && template in t7.templates) {
options.template = t7.templates[template];
}
if (clicked.hasClass('back')) view.router.back(options);
else view.router.load(options);
}
}
$(document).on('click', 'a, .open-panel, .close-panel, .panel-overlay, .modal-overlay, .popup-overlay, .swipeout-delete, .close-popup, .open-popup, .open-popover, .open-login-screen, .close-login-screen .smart-select, .toggle-sortable, .open-sortable, .close-sortable, .accordion-item-toggle', handleClicks);
};
/*======================================================
************ App Resize Actions ************
======================================================*/
// Prevent iPad horizontal body scrolling when soft keyboard is opened
function _fixIpadBodyScrolLeft() {
if (app.device.ipad) {
document.body.scrollLeft = 0;
setTimeout(function () {
document.body.scrollLeft = 0;
}, 0);
}
}
app.initResize = function () {
$(window).on('resize', app.resize);
$(window).on('orientationchange', app.orientationchange);
};
app.resize = function () {
if (app.sizeNavbars) app.sizeNavbars();
_fixIpadBodyScrolLeft();
};
app.orientationchange = function () {
if (app.device && app.device.minimalUi) {
if (window.orientation === 90 || window.orientation === -90) document.body.scrollTop = 0;
}
_fixIpadBodyScrolLeft();
};
/*===============================================================================
************ Store and parse forms data ************
===============================================================================*/
app.formsData = {};
app.formStoreData = function (formId, formJSON) {
// Store form data in app.formsData
app.formsData[formId] = formJSON;
// Store form data in local storage also
app.ls['f7form-' + formId] = JSON.stringify(formJSON);
};
app.formDeleteData = function (formId) {
// Delete form data from app.formsData
if (app.formsData[formId]) {
app.formsData[formId] = '';
delete app.formsData[formId];
}
// Delete form data from local storage also
if (app.ls['f7form-' + formId]) {
app.ls['f7form-' + formId] = '';
app.ls.removeItem('f7form-' + formId);
}
};
app.formGetData = function (formId) {
// First of all check in local storage
if (app.ls['f7form-' + formId]) {
return JSON.parse(app.ls['f7form-' + formId]);
}
// Try to get it from formsData obj
else if (app.formsData[formId]) return app.formsData[formId];
};
app.formToJSON = function (form) {
form = $(form);
if (form.length !== 1) return false;
// Form data
var formData = {};
// Skip input types
var skipTypes = ['submit', 'image', 'button', 'file'];
var skipNames = [];
form.find('input, select, textarea').each(function () {
var input = $(this);
var name = input.attr('name');
var type = input.attr('type');
var tag = this.nodeName.toLowerCase();
if (skipTypes.indexOf(type) >= 0) return;
if (skipNames.indexOf(name) >= 0 || !name) return;
if (tag === 'select' && input.attr('multiple')) {
skipNames.push(name);
formData[name] = [];
form.find('select[name="' + name + '"] option').each(function () {
if (this.selected) formData[name].push(this.value);
});
}
else {
switch (type) {
case 'checkbox' :
skipNames.push(name);
formData[name] = [];
form.find('input[name="' + name + '"]').each(function () {
if (this.checked) formData[name].push(this.value);
});
break;
case 'radio' :
skipNames.push(name);
form.find('input[name="' + name + '"]').each(function () {
if (this.checked) formData[name] = this.value;
});
break;
default :
formData[name] = input.val();
break;
}
}
});
return formData;
};
app.formFromJSON = function (form, formData) {
form = $(form);
if (form.length !== 1) return false;
// Skip input types
var skipTypes = ['submit', 'image', 'button', 'file'];
var skipNames = [];
form.find('input, select, textarea').each(function () {
var input = $(this);
var name = input.attr('name');
var type = input.attr('type');
var tag = this.nodeName.toLowerCase();
if (!formData[name]) return;
if (skipTypes.indexOf(type) >= 0) return;
if (skipNames.indexOf(name) >= 0 || !name) return;
if (tag === 'select' && input.attr('multiple')) {
skipNames.push(name);
form.find('select[name="' + name + '"] option').each(function () {
if (formData[name].indexOf(this.value) >= 0) this.selected = true;
else this.selected = false;
});
}
else {
switch (type) {
case 'checkbox' :
skipNames.push(name);
form.find('input[name="' + name + '"]').each(function () {
if (formData[name].indexOf(this.value) >= 0) this.checked = true;
else this.checked = false;
});
break;
case 'radio' :
skipNames.push(name);
form.find('input[name="' + name + '"]').each(function () {
if (formData[name] === this.value) this.checked = true;
else this.checked = false;
});
break;
default :
input.val(formData[name]);
break;
}
}
});
};
app.initFormsStorage = function (pageContainer) {
pageContainer = $(pageContainer);
if (pageContainer.length === 0) return;
var forms = pageContainer.find('form.store-data');
if (forms.length === 0) return;
// Parse forms data and fill form if there is such data
forms.each(function () {
var id = this.getAttribute('id');
if (!id) return;
var formData = app.formGetData(id);
if (formData) app.formFromJSON(this, formData);
});
// Update forms data on inputs change
function storeForm() {
/*jshint validthis:true */
var form = $(this);
var formId = form[0].id;
if (!formId) return;
var formJSON = app.formToJSON(form);
if (!formJSON) return;
app.formStoreData(formId, formJSON);
form.trigger('store', {data: formJSON});
}
forms.on('change submit', storeForm);
// Detach Listeners
function pageBeforeRemove() {
forms.off('change submit', storeForm);
pageContainer.off('pageBeforeRemove', pageBeforeRemove);
}
pageContainer.on('pageBeforeRemove', pageBeforeRemove);
};
// Ajax submit on forms
$(document).on('submit change', 'form.ajax-submit, form.ajax-submit-onchange', function (e) {
var form = $(this);
if (e.type === 'change' && !form.hasClass('ajax-submit-onchange')) return;
if (e.type === 'submit') e.preventDefault();
var method = form.attr('method') || 'GET';
var contentType = form.attr('enctype');
var url = form.attr('action');
if (!url) return;
var data;
if (method === 'POST') data = new FormData(form[0]);
else data = $.serializeObject(app.formToJSON(form[0]));
var xhr = $.ajax({
method: method,
url: url,
contentType: contentType,
data: data,
success: function (data) {
form.trigger('submitted', {data: data, xhr: xhr});
}
});
});
/*======================================================
************ Handle Browser's History ************
======================================================*/
app.pushStateQueue = [];
app.pushStateClearQueue = function () {
if (app.pushStateQueue.length === 0) return;
var queue = app.pushStateQueue.pop();
var animatePages;
if (app.params.pushStateNoAnimation === true) animatePages = false;
if (queue.action === 'back') {
app.router.back(queue.view, {animatePages: animatePages});
}
if (queue.action === 'loadPage') {
app.router.load(queue.view, {url: queue.stateUrl, animatePages: animatePages, pushState: false});
}
if (queue.action === 'loadContent') {
app.router.load(queue.view, {content: queue.stateContent, animatePages: animatePages, pushState: false});
}
if (queue.action === 'loadPageName') {
app.router.load(queue.view, {pageName: queue.statePageName, animatePages: animatePages, pushState: false});
}
};
app.initPushState = function () {
var blockPopstate = true;
$(window).on('load', function () {
setTimeout(function () {
blockPopstate = false;
}, 0);
});
function handlePopState(e) {
if (blockPopstate) return;
var mainView = app.mainView;
if (!mainView) return;
var state = e.state;
if (!state) {
state = {
viewIndex: app.views.indexOf(mainView),
url : mainView.history[0]
};
}
if (state.viewIndex < 0) return;
var view = app.views[state.viewIndex];
var stateUrl = state && state.url || undefined;
var stateContent = state && state.content || undefined;
var statePageName = state && state.pageName || undefined;
var animatePages;
if (app.params.pushStateNoAnimation === true) animatePages = false;
if (stateUrl !== view.url) {
if (view.history.indexOf(stateUrl) >= 0) {
// Go Back
if (view.allowPageChange) {
app.router.back(view, {url:undefined, animatePages: animatePages, pushState: false, preloadOnly:false});
}
else {
app.pushStateQueue.push({
action: 'back',
view: view
});
}
}
else if (stateContent) {
// Load Page
if (view.allowPageChange) {
app.router.load(view, {content:stateContent, animatePages: animatePages, pushState: false});
}
else {
app.pushStateQueue.unshift({
action: 'loadContent',
stateContent: stateContent,
view: view
});
}
}
else if (statePageName) {
// Load Page by page name with Dom Cache
if (view.allowPageChange) {
app.router.load(view, {pageName:statePageName, animatePages: animatePages, pushState: false});
}
else {
app.pushStateQueue.unshift({
action: 'loadPageName',
statePageName: statePageName,
view: view
});
}
}
else {
// Load Page
if (view.allowPageChange) {
app.router.load(view, {url:stateUrl, animatePages: animatePages, pushState: false});
}
else {
app.pushStateQueue.unshift({
action: 'loadPage',
stateUrl: stateUrl,
view: view
});
}
}
}
}
$(window).on('popstate', handlePopState);
};
/*======================================================
************ Slider ************
======================================================*/
var Slider = function (container, params) {
var defaults = {
initialSlide: 0,
spaceBetween: 0,
speed: 300,
loop: false,
slidesPerView: 1,
onlyExternal: false,
direction: 'horizontal',
paginationHide: true,
slideClass: 'slider-slide',
slideActiveClass: 'slider-slide-active',
slideNextClass: 'slider-slide-next',
slidePrevClass: 'slider-slide-prev',
wrapperClass: 'slider-wrapper',
bulletClass: 'slider-pagination-bullet',
bulletActiveClass: 'slider-pagination-active',
preventClicks: true,
preventClicksPropagation: true,
autoplay: false,
autoplayDisableOnInteraction: true,
};
params = params || {};
for (var def in defaults) {
if (typeof params[def] === 'undefined') {
params[def] = defaults[def];
}
}
var s = this;
s.params = params;
s.container = $(container);
if (s.container.length === 0) return;
s.container[0].f7Slider = s;
if (s.params.direction === 'vertical') {
s.container.addClass('slider-container-vertical');
}
else {
s.container.addClass('slider-container-horizontal');
}
s.wrapper = s.container.children('.' + s.params.wrapperClass);
if (s.params.pagination) {
s.paginationContainer = $(s.params.pagination);
}
s.activeSlideIndex = s.previousSlideIndex = s.params.initialSlide || 0;
var isH = s.params.direction === 'horizontal';
var inverter = isH ? (app.rtl ? -1 : 1) : 1;
s.updateSlides = function () {
s.slides = s.wrapper.children('.' + s.params.slideClass);
if (s.params.spaceBetween !== 0) {
var marginProp = app.rtl ? 'marginLeft' : 'marginRight';
if (isH) {
s.slides.css(marginProp, s.params.spaceBetween + 'px');
}
else s.slides.css({marginBottom: s.params.spaceBetween + 'px'});
}
if (s.params.slidesPerView > 1) {
var sizeValue = '(100% - ' + (s.params.slidesPerView - 1) * params.spaceBetween + 'px)/' + s.params.slidesPerView;
if (isH) {
s.slides.css('width', '-webkit-calc(' + sizeValue + ')');
s.slides.css('width', '-moz-calc(' + sizeValue + ')');
s.slides.css('width', 'calc(' + sizeValue + ')');
}
else {
s.slides.css('height', '-webkit-calc(' + sizeValue + ')');
s.slides.css('height', '-moz-calc(' + sizeValue + ')');
s.slides.css('height', 'calc(' + sizeValue + ')');
}
}
// First/last
s.isFirst = s.isBeginning = s.activeSlideIndex === 0;
s.isLast = s.isEnd = s.activeSlideIndex === s.slides.length - s.params.slidesPerView;
};
s.updatePagination = function () {
if (s.paginationContainer && s.paginationContainer.length > 0) {
var bulletsHTML = '';
var bulletsLength = s.slides.length - s.params.slidesPerView + 1;
if (s.params.loop) {
bulletsLength = s.slides.length - s.loopedSlides * 2;
}
for (var i = 0; i < bulletsLength; i++) {
bulletsHTML += '';
}
s.paginationContainer.html(bulletsHTML);
s.bullets = s.paginationContainer.find('.' + s.params.bulletClass);
}
};
s.updateSize = function () {
s.width = s.container[0].offsetWidth;
s.height = s.container[0].offsetHeight;
s.size = isH ? s.width : s.height;
};
s.attachEvents = function (detach) {
var action = detach ? 'off' : 'on';
// Slide between photos
s.container[action](app.touchEvents.start, s.onTouchStart);
s.container[action](app.touchEvents.move, s.onTouchMove);
s.container[action](app.touchEvents.end, s.onTouchEnd);
$(window)[action]('resize', s.onResize);
// Next, Prev, Index
if (s.params.nextButton) $(s.params.nextButton)[action]('click', s.onClickNext);
if (s.params.prevButton) $(s.params.prevButton)[action]('click', s.onClickPrev);
if (s.params.indexButton) $(s.params.indexButton)[action]('click', s.onClickIndex);
// Prevent Links
if (s.params.preventClicks || s.params.preventClicksPropagation) s.container[action]('click', s.onClick, true);
};
s.detachEvents = function () {
s.attachEvents(true);
};
s.onResize = function () {
s.updateSize();
s.slideTo(s.activeSlideIndex, 0, false);
};
var isTouched, isMoved, touchesStart = {}, touchesCurrent = {}, touchStartTime, isScrolling, currentTranslate;
var lastClickTime = Date.now(), clickTimeout;
s.animating = false;
s.allowClick = true;
s.onClick = function (e) {
if (s.params.preventClicks && !s.allowClick) {
e.preventDefault();
if (s.params.preventClicksPropagation) {
e.stopPropagation();
e.stopImmediatePropagation();
}
}
};
function isFormElement(el) {
var nn = el.nodeName.toLowerCase();
if (nn === 'input' || nn === 'textarea' || nn === 'select') return true;
return false;
}
s.touchedTarget = null;
var hasFocused, hasBlured;
s.onTouchStart = function (e) {
if (s.params.onlyExternal) return;
s.touchedTarget = e.target;
isTouched = true;
isMoved = false;
isScrolling = undefined;
touchesStart.x = touchesCurrent.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
touchesStart.y = touchesCurrent.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
touchStartTime = Date.now();
s.allowClick = true;
s.updateSize();
if (s.params.onTouchStart) s.params.onTouchStart(s, e);
hasFocused = hasBlured = false;
if (e.type === 'mousedown') {
if (!isFormElement(e.target)) e.preventDefault();
}
};
s.onTouchMove = function (e) {
if (s.params.onTouchMove) s.params.onTouchMove(s, e);
s.allowClick = false;
if (e.targetTouches && e.targetTouches.length > 1) return;
touchesCurrent.x = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
touchesCurrent.y = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
if (typeof isScrolling === 'undefined') {
isScrolling = !!(isScrolling || Math.abs(touchesCurrent.y - touchesStart.y) > Math.abs(touchesCurrent.x - touchesStart.x));
}
if ((isH && isScrolling) || (!isH && !isScrolling)) {
if (s.params.onOppositeTouchMove) s.params.onOppositeTouchMove(s, e);
}
if (!isTouched) return;
if ((isH && isScrolling) || (!isH && !isScrolling)) {
isTouched = false;
return;
}
if (s.params.onSliderMove) s.params.onSliderMove(s, e);
e.preventDefault();
e.stopPropagation();
if (!isMoved) {
if (params.loop) {
s.fixLoop();
}
currentTranslate = $.getTranslate(s.wrapper[0], isH ? 'x' : 'y') * inverter;
s.wrapper.transition(0);
if (s.animating) s.onTransitionEnd();
if (params.autoplay && autoplay) {
if (s.params.autoplayDisableOnInteraction) s.stopAutoplay();
else {
if (autoplayTimeout) clearTimeout(autoplayTimeout);
}
}
}
isMoved = true;
var diff = isH ? (touchesCurrent.x - touchesStart.x) * inverter : touchesCurrent.y - touchesStart.y;
if ((diff > 0 && s.activeSlideIndex === 0)) diff = Math.pow(diff, 0.85);
else if (diff < 0 && s.activeSlideIndex === s.slides.length - s.params.slidesPerView) {
diff = -Math.pow(-diff, 0.85);
}
else if (diff < 0 && s.activeSlideIndex === 0 && s.slides.length < s.params.slidesPerView) {
diff = -Math.pow(-diff, 0.85);
}
var translateX = isH ? (diff + currentTranslate) * inverter : 0, translateY = isH ? 0 : diff + currentTranslate;
s.wrapper.transform('translate3d(' + translateX + 'px, ' + translateY + 'px,0)');
};
s.onTouchEnd = function (e) {
if (s.params.onTouchEnd) s.params.onTouchEnd(s, e);
var touchEndTime = Date.now();
var timeDiff = touchEndTime - touchStartTime;
if (isFormElement(s.touchedTarget)) hasFocused = true;
if (document.activeElement && document.activeElement !== s.touchedTarget && isFormElement(document.activeElement)) {
document.activeElement.blur();
hasBlured = true;
}
if (s.allowClick) {
if (timeDiff < 300 && (touchEndTime - lastClickTime) > 300) {
if (clickTimeout) clearTimeout(clickTimeout);
clickTimeout = setTimeout(function () {
if (!s) return;
if (s.params.paginationHide && s.paginationContainer && !hasBlured && !hasFocused) {
s.paginationContainer.toggleClass('slider-pagination-hidden');
}
if (s.params.onClick) s.params.onClick(s, e);
}, 300);
}
if (timeDiff < 300 && (touchEndTime - lastClickTime) < 300) {
if (clickTimeout) clearTimeout(clickTimeout);
if (s.params.onDoubleTap) {
s.params.onDoubleTap(s, e);
}
}
if (s.params.onTap) s.params.onTap(s, e);
}
lastClickTime = Date.now();
if (!isTouched || !isMoved) {
isTouched = isMoved = false;
return;
}
isTouched = isMoved = false;
var touchesDiff = isH ? (touchesCurrent.x - touchesStart.x) * inverter : touchesCurrent.y - touchesStart.y;
//Release links clicks
if (Math.abs(touchesDiff) < 5 && (timeDiff) < 300 && s.allowClick === false) {
s.allowClick = true;
}
setTimeout(function () {
if (!s) return;
s.allowClick = true;
}, 100);
var continueAutoplay = s.params.autoplay && autoplay && !s.params.autoplayDisableOnInteraction;
if (touchesDiff === 0) {
if (continueAutoplay) {
s.startAutoplay();
}
return;
}
var skipSlides = 1;
var slideSize = s.size / s.params.slidesPerView;
if (s.params.slidesPerView > 1) {
skipSlides = Math.abs((Math.abs(touchesDiff) + slideSize / 2) / slideSize);
}
if (continueAutoplay) {
s.wrapper.transitionEnd(function () {
s.startAutoplay();
});
}
if (timeDiff > 300) {
// Long touches
if (touchesDiff <= -slideSize / 2) {
s.slideTo(s.activeSlideIndex + Math.floor(skipSlides));
}
else if (touchesDiff > slideSize / 2) {
s.slideTo(s.activeSlideIndex - Math.floor(skipSlides));
}
else {
s.slideReset();
}
}
else {
if (Math.abs(touchesDiff) < 10) {
s.slideReset();
}
else {
if (touchesDiff < 0) {
s.slideTo(s.activeSlideIndex + Math.round(skipSlides));
}
else {
s.slideTo(s.activeSlideIndex - Math.round(skipSlides));
}
}
}
};
s.slideTo = function (index, speed, runCallbacks) {
if (typeof index === 'undefined') index = 0;
if (index > s.slides.length - s.params.slidesPerView) index = s.slides.length - s.params.slidesPerView;
if (index < 0) index = 0;
var translate = - (s.size + s.params.spaceBetween) * index / s.params.slidesPerView;
if (typeof speed === 'undefined') speed = s.params.speed;
s.previousSlideIndex = s.activeSlideIndex;
s.activeSlideIndex = Math.round(index);
s.isFirst = s.isBeginning = s.activeSlideIndex === 0;
s.isLast = s.isEnd = s.activeSlideIndex === s.slides.length - s.params.slidesPerView;
s.onTransitionStart();
var translateX = isH ? translate * inverter : 0, translateY = isH ? 0 : translate;
if (speed === 0) {
s.wrapper
.transition(0)
.transform('translate3d(' + translateX + 'px,' + translateY + 'px,0)');
if (runCallbacks !== false) s.onTransitionEnd();
}
else {
s.animating = true;
s.wrapper
.transition(speed)
.transform('translate3d(' + translateX + 'px,' + translateY + 'px,0)')
.transitionEnd(function () {
if (runCallbacks !== false) s.onTransitionEnd();
});
}
};
s.updateClasses = function () {
s.slides.removeClass(s.params.slideActiveClass + ' ' + s.params.slideNextClass + ' ' + s.params.slidePrevClass);
var activeSlide = s.slides.eq(s.activeSlideIndex);
activeSlide.addClass(s.params.slideActiveClass);
activeSlide.next().addClass(s.params.slideNextClass);
activeSlide.prev().addClass(s.params.slidePrevClass);
if (s.bullets && s.bullets.length > 0) {
s.bullets.removeClass(s.params.bulletActiveClass);
var activeBulletIndex = s.activeSlideIndex;
if (s.params.loop) {
activeBulletIndex = activeBulletIndex - s.loopedSlides;
if (activeBulletIndex < 0) activeBulletIndex = s.bullets.length + activeBulletIndex;
if (activeBulletIndex >= s.bullets.length) activeBulletIndex = activeBulletIndex - s.bullets.length;
}
s.bullets.eq(activeBulletIndex).addClass(s.params.bulletActiveClass);
}
};
s.onTransitionStart = function () {
s.updateClasses();
if (s.activeSlideIndex !== s.previousSlideIndex) {
if (s.params.onSlideChangeStart) s.params.onSlideChangeStart(s);
}
if (s.params.onTransitionStart) s.params.onTransitionStart(s);
};
s.onTransitionEnd = function () {
s.animating = false;
s.wrapper.transition(0);
if (s.activeSlideIndex !== s.previousSlideIndex) {
if (s.params.onSlideChangeEnd) s.params.onSlideChangeEnd(s);
}
if (s.params.onTransitionEnd) s.params.onTransitionEnd(s);
};
s.slideNext = function () {
if (s.params.loop) {
if (s.animating) return;
s.fixLoop();
setTimeout(function () {
s.slideTo(s.activeSlideIndex + 1);
}, 0);
}
else s.slideTo(s.activeSlideIndex + 1);
};
s.slidePrev = function () {
if (s.params.loop) {
if (s.animating) return;
s.fixLoop();
setTimeout(function () {
s.slideTo(s.activeSlideIndex - 1);
}, 0);
}
else s.slideTo(s.activeSlideIndex - 1);
};
s.slideReset = function () {
s.slideTo(s.activeSlideIndex);
};
// Clicks
s.onClickNext = function (e) {
e.preventDefault();
s.slideNext();
};
s.onClickPrev = function (e) {
e.preventDefault();
s.slidePrev();
};
s.onClickIndex = function (e) {
e.preventDefault();
s.slideTo($(this).index());
};
// Autoplay
var autoplayTimeout;
var autoplay;
s.startAutoplay = function () {
if (!s.params.autoplay) return;
autoplay = true;
if (autoplayTimeout) clearTimeout(autoplayTimeout);
autoplayTimeout = setTimeout(function () {
s.wrapper.transitionEnd(function () {
s.startAutoplay();
});
if (s.params.loop) {
s.slideNext();
}
else {
var index = s.activeSlideIndex + 1;
if (index > s.slides.length - s.params.slidesPerView) index = 0;
s.slideTo(index);
}
}, s.params.autoplay);
};
s.stopAutoplay = function () {
autoplay = false;
if (autoplayTimeout) clearTimeout(autoplayTimeout);
};
s.resetAutoplay = function () {
s.stopAutoplay();
s.startAutoplay();
};
// Create looped slides
s.createLoop = function () {
// Remove duplicated slides
s.wrapper.children('.' + s.params.slideClass+'.slider-slide-duplicate').remove();
var slides = s.wrapper.children('.' + s.params.slideClass);
s.loopedSlides = parseInt(s.params.loopedSlides || s.params.slidesPerView, 10);
if (s.loopedSlides > slides.length) {
s.loopedSlides = slides.length;
return;
}
var prependSlides = [], appendSlides = [], i;
slides.each(function (index, el) {
var slide = $(this);
if (index < s.loopedSlides) appendSlides.push(el);
if (index < slides.length && index >= slides.length - s.loopedSlides) prependSlides.push(el);
slide.attr('data-slider-slide-index', index);
});
for (i = 0; i < appendSlides.length; i++) {
s.wrapper.append($(appendSlides[i].cloneNode(true)).addClass('slider-slide-duplicate'));
}
for (i = prependSlides.length - 1; i >= 0; i--) {
s.wrapper.prepend($(prependSlides[i].cloneNode(true)).addClass('slider-slide-duplicate'));
}
};
s.fixLoop = function () {
var newIndex;
//Fix For Negative Oversliding
if (s.activeSlideIndex < s.loopedSlides) {
newIndex = s.slides.length - s.loopedSlides * 3 + s.activeSlideIndex;
newIndex = newIndex + s.loopedSlides;
s.slideTo(newIndex, 0, false);
}
//Fix For Positive Oversliding
else if (s.activeSlideIndex > s.slides.length - s.params.slidesPerView * 2) {
newIndex = -s.slides.length + s.activeSlideIndex + s.loopedSlides;
newIndex = newIndex + s.loopedSlides;
s.slideTo(newIndex, 0, false);
}
};
// init
s.init = function () {
if (s.params.loop) s.createLoop();
s.updateSlides();
s.updatePagination();
s.updateSize();
if (s.params.loop) {
s.slideTo(s.params.initialSlide + s.loopedSlides, 0, false);
}
else if (s.params.initialSlide > 0) s.slideTo(s.params.initialSlide, 0, false);
else s.updateClasses();
s.attachEvents();
if (s.params.autoplay) s.startAutoplay();
if (typeof s.params.onInit === 'function') {
s.params.onInit();
}
};
s.update = function () {
if (s.params.loop) s.createLoop();
s.updateSlides();
s.updatePagination();
s.updateSize();
s.updateClasses();
};
// Destroy
s.destroy = function () {
s.detachEvents();
if (s.params.onDestroy) s.params.onDestroy();
s = undefined;
};
s.init();
return s;
};
app.slider = function (container, params) {
return new Slider(container, params);
};
app.initSlider = function (pageContainer) {
var page = $(pageContainer);
var sliders = page.find('.slider-init');
if (sliders.length === 0) return;
function destroySliderOnRemove(slider) {
function destroySlider() {
slider.destroy();
page.off('pageBeforeRemove', destroySlider);
}
page.on('pageBeforeRemove', destroySlider);
}
for (var i = 0; i < sliders.length; i++) {
var slider = sliders.eq(i);
var params;
if (slider.data('slider')) {
params = JSON.parse(slider.data('slider'));
}
else {
params = {
initialSlide: parseInt(slider.data('initialSlide'), 10) || undefined,
spaceBetween: parseInt(slider.data('spaceBetween'), 10) || undefined,
speed: parseInt(slider.data('speed'), 10) || undefined,
slidesPerView: parseInt(slider.data('slidesPerView'), 10) || undefined,
direction: slider.data('direction'),
pagination: slider.data('pagination'),
paginationHide: slider.data('paginationHide') && (slider.data('paginationHide') === 'true' ? true : false),
loop: slider.data('loop') && (slider.data('loop') === 'true' ? true : false),
onlyExternal: slider.data('onlyExternal') && (slider.data('onlyExternal') === 'true' ? true : false),
slideClass: slider.data('slideClass'),
slideActiveClass: slider.data('slideActiveClass'),
slideNextClass: slider.data('slideNextClass'),
slidePrevClass: slider.data('slidePrevClass'),
wrapperClass: slider.data('wrapperClass'),
bulletClass: slider.data('bulletClass'),
bulletActiveClass: slider.data('bulletActiveClass'),
nextButton: slider.data('nextButton'),
prevButton: slider.data('prevButton'),
indexButton: slider.data('indexButton'),
autoplay: slider.data('autoplay')
};
}
var _slider = app.slider(slider[0], params);
destroySliderOnRemove(_slider);
}
};
app.reinitSlider = function (pageContainer) {
var page = $(pageContainer);
var sliders = page.find('.slider-init');
if (sliders.length === 0) return;
for (var i = 0; i < sliders.length; i++) {
var sliderInstance = sliders[0].f7Slider;
if (sliderInstance) {
sliderInstance.onResize();
}
}
};
/*======================================================
************ Photo Browser ************
======================================================*/
var PhotoBrowser = function (params) {
var pb = this, i;
var defaults = {
photos : [],
initialSlide: 0,
spaceBetween: 20,
speed: 300,
zoom: true,
maxZoom: 3,
minZoom: 1,
exposition: true,
expositionHideCaptions: false,
type: 'standalone',
navbar: true,
toolbar: true,
theme: 'light',
swipeToClose: true,
backLinkText: 'Close',
ofText: 'of',
loop: false,
lazyLoading: false,
lazyLoadingInPrevNext: false,
lazyLoadingOnTransitionStart: false
};
params = params || {};
for (var def in defaults) {
if (typeof params[def] === 'undefined') {
params[def] = defaults[def];
}
}
pb.params = params;
var iconColor = pb.params.theme === 'dark' ? 'color-white' : '';
var navbarTemplate = pb.params.navbarTemplate ||
'' +
'
' +
'
' +
'
' + pb.params.ofText + '
' +
'
' +
'
' +
'
';
var prevIconClassName = app.rtl ? 'next' : 'prev';
var nextIconClassName = app.rtl ? 'prev' : 'next';
var toolbarTemplate = pb.params.toolbarTemplate ||
'';
var template = pb.params.template ||
'';
var photoTemplate = !pb.params.lazyLoading ?
(pb.params.photoTemplate || '') :
(pb.params.photoLazyTemplate || '');
var captionsTheme = pb.params.captionsTheme || pb.params.theme;
var captionsTemplate = pb.params.captionsTemplate || '{{captions}}
';
var captionTemplate = pb.params.captionTemplate || '{{caption}}
';
var objectTemplate = pb.params.objectTemplate || '{{html}}
';
var photosHtml = '';
var captionsHtml = '';
for (i = 0; i < pb.params.photos.length; i ++) {
var photo = pb.params.photos[i];
var thisTemplate = '';
//check if photo is a string or string-like object, for backwards compatibility
if (typeof(photo) === 'string' || photo instanceof String) {
//check if "photo" is html object
if (photo.indexOf('<') >= 0 || photo.indexOf('>') >= 0) {
thisTemplate = objectTemplate.replace(/{{html}}/g, photo);
} else {
thisTemplate = photoTemplate.replace(/{{url}}/g, photo);
}
//photo is a string, thus has no caption, so remove the caption template placeholder
//otherwise check if photo is an object with a url property
} else if (typeof(photo) === 'object') {
//check if "photo" is html object
if (photo.hasOwnProperty('html') && photo.html.length > 0) {
thisTemplate = objectTemplate.replace(/{{html}}/g, photo.html);
} else if (photo.hasOwnProperty('url') && photo.url.length > 0) {
thisTemplate = photoTemplate.replace(/{{url}}/g, photo.url);
}
//check if photo has a caption
if (photo.hasOwnProperty('caption') && photo.caption.length > 0) {
captionsHtml += captionTemplate.replace(/{{caption}}/g, photo.caption).replace(/{{captionIndex}}/g, i);
} else {
thisTemplate = thisTemplate.replace(/{{caption}}/g, '');
}
}
photosHtml += thisTemplate;
}
var htmlTemplate = template
.replace('{{navbar}}', (pb.params.navbar ? navbarTemplate : ''))
.replace('{{noNavbar}}', (pb.params.navbar ? '' : 'no-navbar'))
.replace('{{photos}}', photosHtml)
.replace('{{captions}}', captionsTemplate.replace(/{{captions}}/g, captionsHtml))
.replace('{{toolbar}}', (pb.params.toolbar ? toolbarTemplate : ''));
pb.activeSlideIndex = pb.params.initialSlide;
pb.openIndex = pb.activeSlideIndex;
pb.opened = false;
pb.open = function (index) {
if (typeof index === 'undefined') index = pb.activeSlideIndex;
index = parseInt(index, 10);
if (pb.opened && pb.slider) {
pb.slider.slideTo(index);
return;
}
pb.opened = true;
pb.openIndex = index;
pb.initialLazyLoaded = false;
if (pb.params.type === 'standalone') {
$('body').append(htmlTemplate);
}
if (pb.params.type === 'popup') {
pb.popup = app.popup('');
$(pb.popup).on('closed', pb.onPopupClose);
}
if (pb.params.type === 'page') {
$(document).on('pageBeforeInit', pb.onPageBeforeInit);
$(document).on('pageBeforeRemove', pb.onPageBeforeRemove);
if (!pb.params.view) pb.params.view = app.mainView;
pb.params.view.loadContent(htmlTemplate);
return;
}
pb.layout(pb.openIndex);
if (pb.params.onOpen) {
pb.params.onOpen(pb);
}
};
pb.close = function () {
pb.opened = false;
if (!pb.sliderContainer || pb.sliderContainer.length === 0) {
return;
}
if (pb.params.onClose) {
pb.params.onClose(pb);
}
// Detach events
pb.attachEvents(true);
// Delete from DOM
if (pb.params.type === 'standalone') {
pb.container.removeClass('photo-browser-in').addClass('photo-browser-out').animationEnd(function () {
pb.container.remove();
});
}
// Destroy slider
pb.slider.destroy();
// Delete references
pb.slider = pb.sliderContainer = pb.sliderWrapper = pb.slides = gestureSlide = gestureImg = gestureImgWrap = undefined;
};
pb.onPopupClose = function (e) {
pb.close();
$(pb.popup).off('pageBeforeInit', pb.onPopupClose);
};
pb.onPageBeforeInit = function (e) {
if (e.detail.page.name === 'photo-browser-slides') {
pb.layout(pb.openIndex);
}
$(document).off('pageBeforeInit', pb.onPageBeforeInit);
};
pb.onPageBeforeRemove = function (e) {
if (e.detail.page.name === 'photo-browser-slides') {
pb.close();
}
$(document).off('pageBeforeRemove', pb.onPageBeforeRemove);
};
pb.loadImageInSlide = function (slider, index) {
if (!slider || typeof index === 'undefined') return;
if (slider.slides.length === 0) return;
var slide = slider.slides.eq(index);
if (!slide.hasClass('photo-browser-slide-lazy')) return;
var img = slide.find('img');
if (img.length === 0) return;
var image = new Image();
var src = img.attr('data-src');
image.onload = function () {
img.attr('src', src);
img.removeAttr('data-src');
slide.removeClass('photo-browser-slide-lazy').find('.preloader').remove();
if (pb.params.onImageLoaded) {
pb.params.onImageLoaded(pb, slide[0], img[0]);
}
};
image.src = src;
if (pb.params.onImageLoad) {
pb.params.onImageLoad(pb, slide[0], img[0]);
}
};
pb.lazyLoading = function (slider, activeIndex) {
pb.loadImageInSlide(slider, activeIndex);
if (pb.params.lazyLoadingInPrevNext) {
var nextSlide = slider.wrapper.find('.slider-slide-next.photo-browser-slide-lazy');
if (nextSlide.length > 0) pb.loadImageInSlide(slider, nextSlide.index());
var prevSlide = slider.wrapper.find('.slider-slide-prev.photo-browser-slide-lazy');
if (prevSlide.length > 0) pb.loadImageInSlide(slider, prevSlide.index());
}
};
pb.onSliderTransitionStart = function (slider) {
pb.activeSlideIndex = slider.activeSlideIndex;
var current = slider.activeSlideIndex + 1;
var total = slider.slides.length;
if (pb.params.loop) {
total = total - 2;
current = current - slider.loopedSlides;
if (current < 1) current = total + current;
if (current > total) current = current - total;
}
pb.container.find('.photo-browser-current').text(current);
pb.container.find('.photo-browser-total').text(total);
$('.photo-browser-prev, .photo-browser-next').removeClass('photo-browser-link-inactive');
if (slider.isFirst && !pb.params.loop) {
$('.photo-browser-prev').addClass('photo-browser-link-inactive');
}
if (slider.isLast && !pb.params.loop) {
$('.photo-browser-next').addClass('photo-browser-link-inactive');
}
// Update captions
if (pb.captions.length > 0) {
pb.captionsContainer.find('.photo-browser-caption-active').removeClass('photo-browser-caption-active');
var captionIndex = pb.params.loop ? slider.slides.eq(slider.activeSlideIndex).attr('data-slider-slide-index') : pb.activeSlideIndex;
pb.captionsContainer.find('[data-caption-index="' + captionIndex + '"]').addClass('photo-browser-caption-active');
}
// Lazy loading
if (pb.params.lazyLoading){
if (pb.params.lazyLoadingOnTransitionStart || (!pb.params.lazyLoadingOnTransitionStart && !pb.initialLazyLoaded)) {
pb.initialLazyLoaded = true;
pb.lazyLoading(slider, pb.activeSlideIndex);
}
}
// Stop Video
var previousSlideVideo = slider.slides.eq(slider.previousSlideIndex).find('video');
if (previousSlideVideo.length > 0) {
if ('pause' in previousSlideVideo[0]) previousSlideVideo[0].pause();
}
// Callback
if (pb.params.onSlideChangeStart) pb.params.onSlideChangeStart(slider);
};
pb.onSliderTransitionEnd = function (slider) {
if (pb.params.lazyLoading && !pb.params.lazyLoadingOnTransitionStart) {
pb.lazyLoading(slider, pb.activeSlideIndex);
}
// Reset zoom
if (pb.params.zoom && gestureSlide && slider.previousSlideIndex !== slider.activeSlideIndex) {
gestureImg.transform('translate3d(0,0,0) scale(1)');
gestureImgWrap.transform('translate3d(0,0,0)');
gestureSlide = gestureImg = gestureImgWrap = undefined;
scale = currentScale = 1;
}
if (pb.params.onSlideChangeEnd) pb.params.onSlideChangeEnd(slider);
};
pb.layout = function (index) {
if (pb.params.type === 'page') {
pb.container = $('.photo-browser-slider-container').parents('.view');
}
else {
pb.container = $('.photo-browser');
}
if (pb.params.type === 'standalone') {
pb.container.addClass('photo-browser-in');
app.sizeNavbars(pb.container);
}
pb.sliderContainer = pb.container.find('.photo-browser-slider-container');
pb.sliderWrapper = pb.container.find('.photo-browser-slider-wrapper');
pb.slides = pb.container.find('.photo-browser-slide');
pb.captionsContainer = pb.container.find('.photo-browser-captions');
pb.captions = pb.container.find('.photo-browser-caption');
var sliderSettings = {
nextButton: pb.params.nextButton || '.photo-browser-next',
prevButton: pb.params.prevButton || '.photo-browser-prev',
indexButton: pb.params.indexButton,
initialSlide: index,
spaceBetween: pb.params.spaceBetween,
speed: pb.params.speed,
loop: pb.params.loop,
onTap: function (slider, e) {
if (pb.params.onTap) pb.params.onTap(slider, e);
},
onClick: function (slider, e) {
if (pb.params.exposition) pb.toggleExposition();
if (pb.params.onClick) pb.params.onClick(slider, e);
},
onDoubleTap: function (slider, e) {
pb.toggleZoom($(e.target).parents('.photo-browser-slide'));
if (pb.params.onDoubleTap) pb.params.onDoubleTap(slider, e);
},
onTransitionStart: function (slider) {
pb.onSliderTransitionStart(slider);
},
onTransitionEnd: function (slider) {
pb.onSliderTransitionEnd(slider);
}
};
if (pb.params.swipeToClose && pb.params.type !== 'page') {
sliderSettings.onTouchStart = pb.swipeCloseTouchStart;
sliderSettings.onOppositeTouchMove = pb.swipeCloseTouchMove;
sliderSettings.onTouchEnd = pb.swipeCloseTouchEnd;
}
pb.slider = app.slider(pb.sliderContainer, sliderSettings);
if (index === 0) {
pb.onSliderTransitionStart(pb.slider);
}
pb.attachEvents();
};
pb.attachEvents = function (detach) {
var action = detach ? 'off' : 'on';
// Slide between photos
if (pb.params.zoom) {
var target = pb.params.loop ? pb.slider.slides : pb.slides;
// Scale image
target[action]('gesturestart', pb.onSlideGestureStart);
target[action]('gesturechange', pb.onSlideGestureChange);
target[action]('gestureend', pb.onSlideGestureEnd);
// Move image
target[action](app.touchEvents.start, pb.onSlideTouchStart);
target[action](app.touchEvents.move, pb.onSlideTouchMove);
target[action](app.touchEvents.end, pb.onSlideTouchEnd);
}
pb.container.find('.photo-browser-close-link')[action]('click', pb.close);
};
var isTouched, isMoved, touchesStart = {}, touchesCurrent = {}, touchStartTime, isScrolling, animating = false, currentTranslate;
var allowClick = true;
// Expose
pb.exposed = false;
pb.toggleExposition = function () {
if (pb.container) pb.container.toggleClass('photo-browser-exposed');
if (pb.params.expositionHideCaptions) pb.captionsContainer.toggleClass('photo-browser-captions-exposed');
pb.exposed = !pb.exposed;
};
pb.enableExposition = function () {
if (pb.container) pb.container.addClass('photo-browser-exposed');
if (pb.params.expositionHideCaptions) pb.captionsContainer.addClass('photo-browser-captions-exposed');
pb.exposed = true;
};
pb.disableExposition = function () {
if (pb.container) pb.container.removeClass('photo-browser-exposed');
if (pb.params.expositionHideCaptions) pb.captionsContainer.removeClass('photo-browser-captions-exposed');
pb.exposed = false;
};
// Gestures
var gestureSlide, gestureImg, gestureImgWrap, scale = 1, currentScale = 1, isScaling = false;
pb.onSlideGestureStart = function (e) {
if (!gestureSlide) {
gestureSlide = $(this);
gestureImg = gestureSlide.find('img, svg, canvas');
gestureImgWrap = gestureImg.parent('.photo-browser-zoom-container');
if (gestureImgWrap.length === 0) {
gestureImg = undefined;
return;
}
}
gestureImg.transition(0);
isScaling = true;
};
pb.onSlideGestureChange = function (e) {
if (!gestureImg || gestureImg.length === 0) return;
scale = e.scale * currentScale;
if (scale > pb.params.maxZoom) {
scale = pb.params.maxZoom - 1 + Math.pow((scale - pb.params.maxZoom + 1), 0.5);
}
if (scale < pb.params.minZoom) {
scale = pb.params.minZoom + 1 - Math.pow((pb.params.minZoom - scale + 1), 0.5);
}
gestureImg.transform('translate3d(0,0,0) scale(' + scale + ')');
};
pb.onSlideGestureEnd = function (e) {
if (!gestureImg || gestureImg.length === 0) return;
scale = Math.max(Math.min(scale, pb.params.maxZoom), pb.params.minZoom);
gestureImg.transition(pb.params.speed).transform('translate3d(0,0,0) scale(' + scale + ')');
currentScale = scale;
isScaling = false;
if (scale === 1) gestureSlide = undefined;
};
pb.toggleZoom = function () {
if (!gestureSlide) {
gestureSlide = pb.slider.slides.eq(pb.slider.activeSlideIndex);
gestureImg = gestureSlide.find('img, svg, canvas');
gestureImgWrap = gestureImg.parent('.photo-browser-zoom-container');
}
if (!gestureImg || gestureImg.length === 0) return;
gestureImgWrap.transition(300).transform('translate3d(0,0,0)');
if (scale && scale !== 1) {
scale = currentScale = 1;
gestureImg.transition(300).transform('translate3d(0,0,0) scale(1)');
gestureSlide = undefined;
}
else {
scale = currentScale = pb.params.maxZoom;
gestureImg.transition(300).transform('translate3d(0,0,0) scale(' + scale + ')');
}
};
var imageIsTouched, imageIsMoved, imageCurrentX, imageCurrentY, imageMinX, imageMinY, imageMaxX, imageMaxY, imageWidth, imageHeight, imageTouchesStart = {}, imageTouchesCurrent = {}, imageStartX, imageStartY, velocityPrevPositionX, velocityPrevTime, velocityX, velocityPrevPositionY, velocityY;
pb.onSlideTouchStart = function (e) {
if (!gestureImg || gestureImg.length === 0) return;
if (imageIsTouched) return;
if (app.device.os === 'android') e.preventDefault();
imageIsTouched = true;
imageTouchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
imageTouchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
};
pb.onSlideTouchMove = function (e) {
if (!gestureImg || gestureImg.length === 0) return;
pb.slider.allowClick = false;
if (!imageIsTouched || !gestureSlide) return;
if (!imageIsMoved) {
imageWidth = gestureImg[0].offsetWidth;
imageHeight = gestureImg[0].offsetHeight;
imageStartX = $.getTranslate(gestureImgWrap[0], 'x') || 0;
imageStartY = $.getTranslate(gestureImgWrap[0], 'y') || 0;
gestureImgWrap.transition(0);
}
// Define if we need image drag
var scaledWidth = imageWidth * scale;
var scaledHeight = imageHeight * scale;
if (scaledWidth < pb.slider.width && scaledHeight < pb.slider.height) return;
imageMinX = Math.min((pb.slider.width / 2 - scaledWidth / 2), 0);
imageMaxX = -imageMinX;
imageMinY = Math.min((pb.slider.height / 2 - scaledHeight / 2), 0);
imageMaxY = -imageMinY;
imageTouchesCurrent.x = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
imageTouchesCurrent.y = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
if (!imageIsMoved && !isScaling) {
if (
(Math.floor(imageMinX) === Math.floor(imageStartX) && imageTouchesCurrent.x < imageTouchesStart.x) ||
(Math.floor(imageMaxX) === Math.floor(imageStartX) && imageTouchesCurrent.x > imageTouchesStart.x)
) {
imageIsTouched = false;
return;
}
}
e.stopPropagation();
imageIsMoved = true;
imageCurrentX = imageTouchesCurrent.x - imageTouchesStart.x + imageStartX;
imageCurrentY = imageTouchesCurrent.y - imageTouchesStart.y + imageStartY;
if (imageCurrentX < imageMinX) {
imageCurrentX = imageMinX + 1 - Math.pow((imageMinX - imageCurrentX + 1), 0.8);
}
if (imageCurrentX > imageMaxX) {
imageCurrentX = imageMaxX - 1 + Math.pow((imageCurrentX - imageMaxX + 1), 0.8);
}
if (imageCurrentY < imageMinY) {
imageCurrentY = imageMinY + 1 - Math.pow((imageMinY - imageCurrentY + 1), 0.8);
}
if (imageCurrentY > imageMaxY) {
imageCurrentY = imageMaxY - 1 + Math.pow((imageCurrentY - imageMaxY + 1), 0.8);
}
//Velocity
if (!velocityPrevPositionX) velocityPrevPositionX = imageTouchesCurrent.x;
if (!velocityPrevPositionY) velocityPrevPositionY = imageTouchesCurrent.y;
if (!velocityPrevTime) velocityPrevTime = Date.now();
velocityX = (imageTouchesCurrent.x - velocityPrevPositionX) / (Date.now() - velocityPrevTime) / 2;
velocityY = (imageTouchesCurrent.y - velocityPrevPositionY) / (Date.now() - velocityPrevTime) / 2;
if (Math.abs(imageTouchesCurrent.x - velocityPrevPositionX) < 2) velocityX = 0;
if (Math.abs(imageTouchesCurrent.y - velocityPrevPositionY) < 2) velocityY = 0;
velocityPrevPositionX = imageTouchesCurrent.x;
velocityPrevPositionY = imageTouchesCurrent.y;
velocityPrevTime = Date.now();
gestureImgWrap.transform('translate3d(' + imageCurrentX + 'px, ' + imageCurrentY + 'px,0)');
};
pb.onSlideTouchEnd = function (e) {
if (!gestureImg || gestureImg.length === 0) return;
if (!imageIsTouched || !imageIsMoved) {
imageIsTouched = false;
imageIsMoved = false;
return;
}
imageIsTouched = false;
imageIsMoved = false;
var momentumDurationX = 300;
var momentumDurationY = 300;
var momentumDistanceX = velocityX * momentumDurationX;
var newPositionX = imageCurrentX + momentumDistanceX;
var momentumDistanceY = velocityY * momentumDurationY;
var newPositionY = imageCurrentY + momentumDistanceY;
//Fix duration
if (velocityX !== 0) momentumDurationX = Math.abs((newPositionX - imageCurrentX) / velocityX);
if (velocityY !== 0) momentumDurationY = Math.abs((newPositionY - imageCurrentY) / velocityY);
var momentumDuration = Math.max(momentumDurationX, momentumDurationY);
imageCurrentX = newPositionX;
imageCurrentY = newPositionY;
// Define if we need image drag
var scaledWidth = imageWidth * scale;
var scaledHeight = imageHeight * scale;
imageMinX = Math.min((pb.slider.width / 2 - scaledWidth / 2), 0);
imageMaxX = -imageMinX;
imageMinY = Math.min((pb.slider.height / 2 - scaledHeight / 2), 0);
imageMaxY = -imageMinY;
imageCurrentX = Math.max(Math.min(imageCurrentX, imageMaxX), imageMinX);
imageCurrentY = Math.max(Math.min(imageCurrentY, imageMaxY), imageMinY);
gestureImgWrap.transition(momentumDuration).transform('translate3d(' + imageCurrentX + 'px, ' + imageCurrentY + 'px,0)');
};
// Swipe Up To Close
var swipeToCloseIsTouched = false;
var allowSwipeToClose = true;
var swipeToCloseDiff, swipeToCloseStart, swipeToCloseCurrent, swipeToCloseStarted = false, swipeToCloseActiveSlide, swipeToCloseTimeStart;
pb.swipeCloseTouchStart = function (slider, e) {
if (!allowSwipeToClose) return;
swipeToCloseIsTouched = true;
};
pb.swipeCloseTouchMove = function (slider, e) {
if (!swipeToCloseIsTouched) return;
if (!swipeToCloseStarted) {
swipeToCloseStarted = true;
swipeToCloseStart = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
swipeToCloseActiveSlide = pb.slider.slides.eq(pb.slider.activeSlideIndex);
swipeToCloseTimeStart = (new Date()).getTime();
}
e.preventDefault();
swipeToCloseCurrent = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
swipeToCloseDiff = swipeToCloseStart - swipeToCloseCurrent;
var opacity = 1 - Math.abs(swipeToCloseDiff) / 300;
swipeToCloseActiveSlide.transform('translate3d(0,' + (-swipeToCloseDiff) + 'px,0)');
pb.slider.container.css('opacity', opacity).transition(0);
};
pb.swipeCloseTouchEnd = function (slider, e) {
swipeToCloseIsTouched = false;
if (!swipeToCloseStarted) {
swipeToCloseStarted = false;
return;
}
swipeToCloseStarted = false;
allowSwipeToClose = false;
var diff = Math.abs(swipeToCloseDiff);
var timeDiff = (new Date()).getTime() - swipeToCloseTimeStart;
if ((timeDiff < 300 && diff > 20) || (timeDiff >= 300 && diff > 100)) {
setTimeout(function () {
if (pb.params.type === 'standalone') {
pb.close();
}
if (pb.params.type === 'popup') {
app.closeModal(pb.popup);
}
if (pb.params.onSwipeToClose) {
pb.params.onSwipeToClose(pb);
}
allowSwipeToClose = true;
}, 0);
return;
}
if (diff !== 0) {
swipeToCloseActiveSlide.addClass('transitioning').transitionEnd(function () {
allowSwipeToClose = true;
swipeToCloseActiveSlide.removeClass('transitioning');
});
}
else {
allowSwipeToClose = true;
}
pb.slider.container.css('opacity', '').transition('');
swipeToCloseActiveSlide.transform('');
};
return pb;
};
app.photoBrowser = function (params) {
return new PhotoBrowser(params);
};
/*======================================================
************ Notifications ************
======================================================*/
var _tempNotificationElement;
app.addNotification = function (params) {
if (!params) return;
if (typeof params.media === 'undefined') params.media = app.params.notificationMedia;
if (typeof params.title === 'undefined') params.title = app.params.notificationTitle;
if (typeof params.subtitle === 'undefined') params.subtitle = app.params.notificationSubtitle;
if (typeof params.closeIcon === 'undefined') params.closeIcon = app.params.notificationCloseIcon;
if (typeof params.hold === 'undefined') params.hold = app.params.notificationHold;
if (typeof params.closeOnClick === 'undefined') params.closeOnClick = app.params.notificationCloseOnClick;
if (!_tempNotificationElement) _tempNotificationElement = document.createElement('div');
var container = $('.notifications');
if (container.length === 0) {
$('body').append('');
container = $('.notifications');
}
var list = container.children('ul');
var itemHTML;
if (params.custom) {
itemHTML = '- ' + params.custom + '
';
}
else {
itemHTML = '' +
(params.media ?
'
' +
params.media +
'
' : '') +
'
' +
'
' +
(params.title ? '
' + params.title + '
' : '') +
(params.closeIcon ? '
' : '') +
'
' +
(params.subtitle ? '
' + params.subtitle + '
' : '') +
(params.message ? '
' + params.message + '
' : '') +
'
' +
'
';
}
_tempNotificationElement.innerHTML = itemHTML;
var item = $(_tempNotificationElement).children();
item.on('click', function (e) {
var close = false;
if ($(e.target).is('.close-notification') || $(e.target).parents('.close-notification').length > 0) {
close = true;
}
else {
if (params.onClick) params.onClick(e, item[0]);
if (params.closeOnClick) close = true;
}
if (close) app.closeNotification(item[0]);
});
if (params.onClose) {
item.data('f7NotificationOnClose', function () {
params.onClose(item[0]);
});
}
if (params.additionalClass) {
item.addClass(params.additionalClass);
}
if (params.hold) {
setTimeout(function () {
if (item.length > 0) app.closeNotification(item[0]);
}, params.hold);
}
list.prepend(item[0]);
container.show();
var itemHeight = item.outerHeight();
item.css('marginTop', -itemHeight + 'px');
item.transition(0);
var clientLeft = item[0].clientLeft;
item.transition('');
item.css('marginTop', '0px');
container.transform('translate3d(0, 0,0)');
item.removeClass('notification-hidden');
return item[0];
};
app.closeNotification = function (item) {
item = $(item);
if (item.length === 0) return;
if (item.hasClass('notification-item-removing')) return;
var container = $('.notifications');
var itemHeight = item.outerHeight();
item.css('height', itemHeight + 'px').transition(0);
var clientLeft = item[0].clientLeft;
item.css('height', '0px').transition('').addClass('notification-item-removing');
if (item.data('f7NotificationOnClose')) item.data('f7NotificationOnClose')();
if (container.find('.notification-item:not(.notification-item-removing)').length === 0) {
container.transform('');
}
item.addClass('notification-hidden').transitionEnd(function () {
item.remove();
if (container.find('.notification-item').length === 0) {
container.hide();
}
});
};
/*===========================
Compile Template7 Templates On App Init
===========================*/
app.initTemplate7Templates = function () {
if (!window.Template7) return;
Template7.templates = Template7.templates || app.params.templates || {};
Template7.data = Template7.data || app.params.template7Data || {};
Template7.cache = Template7.cache || {};
app.templates = Template7.templates;
app.template7Data = Template7.data;
app.template7Cache = Template7.cache;
// Precompile templates on app init
if (!app.params.precompileTemplates) return;
$('script[type="text/template7"]').each(function () {
var id = $(this).attr('id');
if (!id) return;
Template7.templates[id] = Template7.compile($(this).html());
});
};
/*=======================================
************ Plugins API ************
=======================================*/
var _plugins = [];
app.initPlugins = function () {
// Initialize plugins
for (var plugin in app.plugins) {
var p = app.plugins[plugin](app, app.params[plugin]);
if (p) _plugins.push(p);
}
};
// Plugin Hooks
app.pluginHook = function (hook) {
for (var i = 0; i < _plugins.length; i++) {
if (_plugins[i].hooks && hook in _plugins[i].hooks) {
_plugins[i].hooks[hook](arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
}
}
};
// Prevented by plugin
app.pluginPrevent = function (action) {
var prevent = false;
for (var i = 0; i < _plugins.length; i++) {
if (_plugins[i].prevents && action in _plugins[i].prevents) {
if (_plugins[i].prevents[action](arguments[1], arguments[2], arguments[3], arguments[4], arguments[5])) prevent = true;
}
}
return prevent;
};
// Preprocess content by plugin
app.pluginProcess = function (action, data) {
var processed = data;
for (var i = 0; i < _plugins.length; i++) {
if (_plugins[i].preprocess && process in _plugins[i].preprocess) {
processed = _plugins[i].preprocess[process](data, arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]);
}
}
return processed;
};
/*======================================================
************ App Init ************
======================================================*/
app.init = function () {
// Init Plugins
if (app.initPlugins) app.initPlugins();
// Init Device
if (app.getDeviceInfo) app.getDeviceInfo();
// Init Click events
if (app.initFastClicks && app.params.fastClicks) app.initFastClicks();
if (app.initClickEvents) app.initClickEvents();
// Init each page callbacks
$('.page:not(.cached)').each(function () {
var pageContainer = $(this);
var viewContainer = pageContainer.parents('.' + app.params.viewClass);
if (viewContainer.length === 0) return;
var view = viewContainer[0].f7View || false;
var url = view && view.url ? view.url : false;
if (viewContainer) {
viewContainer.attr('data-page', pageContainer.attr('data-page') || undefined);
}
app.pageInitCallback(view, {pageContainer: this, url: url, position: 'center'});
});
// Init resize events
if (app.initResize) app.initResize();
// Init push state
if (app.initPushState && app.params.pushState) app.initPushState();
// Init Live Swipeouts events
if (app.initSwipeout && app.params.swipeout) app.initSwipeout();
// Init Live Sortable events
if (app.initSortable && app.params.sortable) app.initSortable();
// Init Live Swipe Panels
if (app.initSwipePanels && (app.params.swipePanel || app.params.swipePanelOnlyClose)) app.initSwipePanels();
// App Init callback
if (app.params.onAppInit) app.params.onAppInit();
// Compile Template7 templates on app load
if (app.initTemplate7Templates) app.initTemplate7Templates();
// Plugin app init hook
app.pluginHook('appInit');
};
if (app.params.init) app.init();
//Return instance
return app;
};
/*===========================
Dom7 Library
===========================*/
var Dom7 = (function () {
var Dom7 = function (arr) {
var _this = this, i = 0;
// Create array-like object
for (i = 0; i < arr.length; i++) {
_this[i] = arr[i];
}
_this.length = arr.length;
// Return collection with methods
return this;
};
var $ = function (selector, context) {
var arr = [], i = 0;
if (selector && !context) {
if (selector instanceof Dom7) {
return selector;
}
}
if (selector) {
// String
if (typeof selector === 'string') {
var els, tempParent, html = selector.trim();
if (html.indexOf('<') >= 0 && html.indexOf('>') >= 0) {
var toCreate = 'div';
if (html.indexOf('- :~]/)) {
// Pure ID selector
els = [document.getElementById(selector.split('#')[1])];
}
else {
// Other selectors
els = (context || document).querySelectorAll(selector);
}
for (i = 0; i < els.length; i++) {
if (els[i]) arr.push(els[i]);
}
}
}
// Node/element
else if (selector.nodeType || selector === window || selector === document) {
arr.push(selector);
}
//Array of elements or instance of Dom
else if (selector.length > 0 && selector[0].nodeType) {
for (i = 0; i < selector.length; i++) {
arr.push(selector[i]);
}
}
}
return new Dom7(arr);
};
Dom7.prototype = {
// Classes and attriutes
addClass: function (className) {
if (typeof className === 'undefined') {
return this;
}
var classes = className.split(' ');
for (var i = 0; i < classes.length; i++) {
for (var j = 0; j < this.length; j++) {
this[j].classList.add(classes[i]);
}
}
return this;
},
removeClass: function (className) {
var classes = className.split(' ');
for (var i = 0; i < classes.length; i++) {
for (var j = 0; j < this.length; j++) {
this[j].classList.remove(classes[i]);
}
}
return this;
},
hasClass: function (className) {
if (!this[0]) return false;
else return this[0].classList.contains(className);
},
toggleClass: function (className) {
var classes = className.split(' ');
for (var i = 0; i < classes.length; i++) {
for (var j = 0; j < this.length; j++) {
this[j].classList.toggle(classes[i]);
}
}
return this;
},
attr: function (attrs, value) {
if (arguments.length === 1 && typeof attrs === 'string') {
// Get attr
if (this[0]) return this[0].getAttribute(attrs);
else return undefined;
}
else {
// Set attrs
for (var i = 0; i < this.length; i++) {
if (arguments.length === 2) {
// String
this[i].setAttribute(attrs, value);
}
else {
// Object
for (var attrName in attrs) {
this[i][attrName] = attrs[attrName];
this[i].setAttribute(attrName, attrs[attrName]);
}
}
}
return this;
}
},
removeAttr: function (attr) {
for (var i = 0; i < this.length; i++) {
this[i].removeAttribute(attr);
}
},
prop: function (props, value) {
if (arguments.length === 1 && typeof props === 'string') {
// Get prop
if (this[0]) return this[0][props];
else return undefined;
}
else {
// Set props
for (var i = 0; i < this.length; i++) {
if (arguments.length === 2) {
// String
this[i][props] = value;
}
else {
// Object
for (var propName in props) {
this[i][propName] = props[propName];
}
}
}
return this;
}
},
data: function (key, value) {
if (typeof value === 'undefined') {
// Get value
if (this[0]) {
var dataKey = this[0].getAttribute('data-' + key);
if (dataKey) return dataKey;
else if (this[0].dom7ElementDataStorage && (key in this[0].dom7ElementDataStorage)) return this[0].dom7ElementDataStorage[key];
else return undefined;
}
else return undefined;
}
else {
// Set value
for (var i = 0; i < this.length; i++) {
var el = this[i];
if (!el.dom7ElementDataStorage) el.dom7ElementDataStorage = {};
el.dom7ElementDataStorage[key] = value;
}
return this;
}
},
val: function (value) {
if (typeof value === 'undefined') {
if (this[0]) return this[0].value;
else return null;
}
else {
for (var i = 0; i < this.length; i++) {
this[i].value = value;
}
return this;
}
},
// Transforms
transform : function (transform) {
for (var i = 0; i < this.length; i++) {
var elStyle = this[i].style;
elStyle.webkitTransform = elStyle.MsTransform = elStyle.msTransform = elStyle.MozTransform = elStyle.OTransform = elStyle.transform = transform;
}
return this;
},
transition: function (duration) {
if (typeof duration !== 'string') {
duration = duration + 'ms';
}
for (var i = 0; i < this.length; i++) {
var elStyle = this[i].style;
elStyle.webkitTransitionDuration = elStyle.MsTransitionDuration = elStyle.msTransitionDuration = elStyle.MozTransitionDuration = elStyle.OTransitionDuration = elStyle.transitionDuration = duration;
}
return this;
},
//Events
on: function (eventName, targetSelector, listener, capture) {
function handleLiveEvent(e) {
var target = e.target;
if ($(target).is(targetSelector)) listener.call(target, e);
else {
var parents = $(target).parents();
for (var k = 0; k < parents.length; k++) {
if ($(parents[k]).is(targetSelector)) listener.call(parents[k], e);
}
}
}
var events = eventName.split(' ');
var i, j;
for (i = 0; i < this.length; i++) {
if (typeof targetSelector === 'function' || targetSelector === false) {
// Usual events
if (typeof targetSelector === 'function') {
listener = arguments[1];
capture = arguments[2] || false;
}
for (j = 0; j < events.length; j++) {
this[i].addEventListener(events[j], listener, capture);
}
}
else {
//Live events
for (j = 0; j < events.length; j++) {
if (!this[i].dom7LiveListeners) this[i].dom7LiveListeners = [];
this[i].dom7LiveListeners.push({listener: listener, liveListener: handleLiveEvent});
this[i].addEventListener(events[j], handleLiveEvent, capture);
}
}
}
return this;
},
off: function (eventName, targetSelector, listener, capture) {
var events = eventName.split(' ');
for (var i = 0; i < events.length; i++) {
for (var j = 0; j < this.length; j++) {
if (typeof targetSelector === 'function' || targetSelector === false) {
// Usual events
if (typeof targetSelector === 'function') {
listener = arguments[1];
capture = arguments[2] || false;
}
this[j].removeEventListener(events[i], listener, capture);
}
else {
// Live event
if (this[j].dom7LiveListeners) {
for (var k = 0; k < this[j].dom7LiveListeners.length; k++) {
if (this[j].dom7LiveListeners[k].listener === listener) {
this[j].removeEventListener(events[i], this[j].dom7LiveListeners[k].liveListener, capture);
}
}
}
}
}
}
return this;
},
once: function (eventName, targetSelector, listener, capture) {
var dom = this;
if (typeof targetSelector === 'function') {
targetSelector = false;
listener = arguments[1];
capture = arguments[2];
}
function proxy(e) {
listener(e);
dom.off(eventName, targetSelector, proxy, capture);
}
dom.on(eventName, targetSelector, proxy, capture);
},
trigger: function (eventName, eventData) {
for (var i = 0; i < this.length; i++) {
var evt;
try {
evt = new CustomEvent(eventName, {detail: eventData, bubbles: true, cancelable: true});
}
catch (e) {
evt = document.createEvent('Event');
evt.initEvent(eventName, true, true);
evt.detail = eventData;
}
this[i].dispatchEvent(evt);
}
return this;
},
transitionEnd: function (callback) {
var events = ['webkitTransitionEnd', 'transitionend', 'oTransitionEnd', 'MSTransitionEnd', 'msTransitionEnd'],
i, j, dom = this;
function fireCallBack(e) {
/*jshint validthis:true */
if (e.target !== this) return;
callback.call(this, e);
for (i = 0; i < events.length; i++) {
dom.off(events[i], fireCallBack);
}
}
if (callback) {
for (i = 0; i < events.length; i++) {
dom.on(events[i], fireCallBack);
}
}
return this;
},
animationEnd: function (callback) {
var events = ['webkitAnimationEnd', 'OAnimationEnd', 'MSAnimationEnd', 'animationend'],
i, j, dom = this;
function fireCallBack(e) {
callback(e);
for (i = 0; i < events.length; i++) {
dom.off(events[i], fireCallBack);
}
}
if (callback) {
for (i = 0; i < events.length; i++) {
dom.on(events[i], fireCallBack);
}
}
return this;
},
// Sizing/Styles
width: function () {
if (this[0] === window) {
return window.innerWidth;
}
else {
if (this.length > 0) {
return parseFloat(this.css('width'));
}
else {
return null;
}
}
},
outerWidth: function (includeMargins) {
if (this.length > 0) {
if (includeMargins)
return this[0].offsetWidth + parseFloat(this.css('margin-right')) + parseFloat(this.css('margin-left'));
else
return this[0].offsetWidth;
}
else return null;
},
height: function () {
if (this[0] === window) {
return window.innerHeight;
}
else {
if (this.length > 0) {
return parseFloat(this.css('height'));
}
else {
return null;
}
}
},
outerHeight: function (includeMargins) {
if (this.length > 0) {
if (includeMargins)
return this[0].offsetHeight + parseFloat(this.css('margin-top')) + parseFloat(this.css('margin-bottom'));
else
return this[0].offsetHeight;
}
else return null;
},
offset: function () {
if (this.length > 0) {
var el = this[0];
var box = el.getBoundingClientRect();
var body = document.body;
var clientTop = el.clientTop || body.clientTop || 0;
var clientLeft = el.clientLeft || body.clientLeft || 0;
var scrollTop = window.pageYOffset || el.scrollTop;
var scrollLeft = window.pageXOffset || el.scrollLeft;
return {
top: box.top + scrollTop - clientTop,
left: box.left + scrollLeft - clientLeft
};
}
else {
return null;
}
},
hide: function () {
for (var i = 0; i < this.length; i++) {
this[i].style.display = 'none';
}
return this;
},
show: function () {
for (var i = 0; i < this.length; i++) {
this[i].style.display = 'block';
}
return this;
},
css: function (props, value) {
var i;
if (arguments.length === 1) {
if (typeof props === 'string') {
if (this[0]) return window.getComputedStyle(this[0], null).getPropertyValue(props);
}
else {
for (i = 0; i < this.length; i++) {
for (var prop in props) {
this[i].style[prop] = props[prop];
}
}
return this;
}
}
if (arguments.length === 2 && typeof props === 'string') {
for (i = 0; i < this.length; i++) {
this[i].style[props] = value;
}
return this;
}
return this;
},
//Dom manipulation
each: function (callback) {
for (var i = 0; i < this.length; i++) {
callback.call(this[i], i, this[i]);
}
return this;
},
html: function (html) {
if (typeof html === 'undefined') {
return this[0] ? this[0].innerHTML : undefined;
}
else {
for (var i = 0; i < this.length; i++) {
this[i].innerHTML = html;
}
return this;
}
},
text: function (text) {
if (typeof text === 'undefined') {
if (this[0]) {
return this[0].textContent.trim();
}
else return null;
}
else {
for (var i = 0; i < this.length; i++) {
this[0].textContent = text;
}
}
},
is: function (selector) {
if (!this[0]) return false;
var compareWith, i;
if (typeof selector === 'string') {
var el = this[0];
if (el === document) return selector === document;
if (el === window) return selector === window;
if (el.matches) return el.matches(selector);
else if (el.webkitMatchesSelector) return el.webkitMatchesSelector(selector);
else if (el.mozMatchesSelector) return el.mozMatchesSelector(selector);
else if (el.msMatchesSelector) return el.msMatchesSelector(selector);
else {
compareWith = $(selector);
for (i = 0; i < compareWith.length; i++) {
if (compareWith[i] === this[0]) return true;
}
return false;
}
}
else if (selector === document) return this[0] === document;
else if (selector === window) return this[0] === window;
else {
if (selector.nodeType || selector instanceof Dom7) {
compareWith = selector.nodeType ? [selector] : selector;
for (i = 0; i < compareWith.length; i++) {
if (compareWith[i] === this[0]) return true;
}
return false;
}
return false;
}
},
indexOf: function (el) {
for (var i = 0; i < this.length; i++) {
if (this[i] === el) return i;
}
},
index: function () {
if (this[0]) {
var child = this[0];
var i = 0;
while ((child = child.previousSibling) !== null) {
if (child.nodeType === 1) i++;
}
return i;
}
else return undefined;
},
eq: function (index) {
if (typeof index === 'undefined') return this;
var length = this.length;
var returnIndex;
if (index > length - 1) {
return new Dom7([]);
}
if (index < 0) {
returnIndex = length + index;
if (returnIndex < 0) return new Dom7([]);
else return new Dom7([this[returnIndex]]);
}
return new Dom7([this[index]]);
},
append: function (newChild) {
var i, j;
for (i = 0; i < this.length; i++) {
if (typeof newChild === 'string') {
var tempDiv = document.createElement('div');
tempDiv.innerHTML = newChild;
while (tempDiv.firstChild) {
this[i].appendChild(tempDiv.firstChild);
}
}
else if (newChild instanceof Dom7) {
for (j = 0; j < newChild.length; j++) {
this[i].appendChild(newChild[j]);
}
}
else {
this[i].appendChild(newChild);
}
}
return this;
},
prepend: function (newChild) {
var i, j;
for (i = 0; i < this.length; i++) {
if (typeof newChild === 'string') {
var tempDiv = document.createElement('div');
tempDiv.innerHTML = newChild;
for (j = tempDiv.childNodes.length - 1; j >= 0; j--) {
this[i].insertBefore(tempDiv.childNodes[j], this[i].childNodes[0]);
}
// this[i].insertAdjacentHTML('afterbegin', newChild);
}
else if (newChild instanceof Dom7) {
for (j = 0; j < newChild.length; j++) {
this[i].insertBefore(newChild[j], this[i].childNodes[0]);
}
}
else {
this[i].insertBefore(newChild, this[i].childNodes[0]);
}
}
return this;
},
insertBefore: function (selector) {
var before = $(selector);
for (var i = 0; i < this.length; i++) {
if (before.length === 1) {
before[0].parentNode.insertBefore(this[i], before[0]);
}
else if (before.length > 1) {
for (var j = 0; j < before.length; j++) {
before[j].parentNode.insertBefore(this[i].cloneNode(true), before[j]);
}
}
}
},
insertAfter: function (selector) {
var after = $(selector);
for (var i = 0; i < this.length; i++) {
if (after.length === 1) {
after[0].parentNode.insertBefore(this[i], after[0].nextSibling);
}
else if (after.length > 1) {
for (var j = 0; j < after.length; j++) {
after[j].parentNode.insertBefore(this[i].cloneNode(true), after[j].nextSibling);
}
}
}
},
next: function (selector) {
if (this.length > 0) {
if (selector) {
if (this[0].nextElementSibling && $(this[0].nextElementSibling).is(selector)) return new Dom7([this[0].nextElementSibling]);
else return new Dom7([]);
}
else {
if (this[0].nextElementSibling) return new Dom7([this[0].nextElementSibling]);
else return new Dom7([]);
}
}
else return new Dom7([]);
},
nextAll: function (selector) {
var nextEls = [];
var el = this[0];
if (!el) return new Dom7([]);
while (el.nextElementSibling) {
var next = el.nextElementSibling;
if (selector && $(next).is(selector)) nextEls.push(next);
else nextEls.push(next);
el = next;
}
return new Dom7(nextEls);
},
prev: function (selector) {
if (this.length > 0) {
if (selector) {
if (this[0].previousElementSibling && $(this[0].previousElementSibling).is(selector)) return new Dom7([this[0].previousElementSibling]);
else return new Dom7([]);
}
else {
if (this[0].previousElementSibling) return new Dom7([this[0].previousElementSibling]);
else return new Dom7([]);
}
}
else return new Dom7([]);
},
prevAll: function (selector) {
var prevEls = [];
var el = this[0];
if (!el) return new Dom7([]);
while (el.previousElementSibling) {
var prev = el.previousElementSibling;
if (selector && $(prev).is(selector)) prevEls.push(prev);
else prevEls.push(prev);
el = prev;
}
return new Dom7(prevEls);
},
parent: function (selector) {
var parents = [];
for (var i = 0; i < this.length; i++) {
if (selector) {
if ($(this[i].parentNode).is(selector)) parents.push(this[i].parentNode);
}
else {
parents.push(this[i].parentNode);
}
}
return $($.unique(parents));
},
parents: function (selector) {
var parents = [];
for (var i = 0; i < this.length; i++) {
var parent = this[i].parentNode;
while (parent) {
if (selector) {
if ($(parent).is(selector)) parents.push(parent);
}
else {
parents.push(parent);
}
parent = parent.parentNode;
}
}
return $($.unique(parents));
},
find : function (selector) {
var foundElements = [];
for (var i = 0; i < this.length; i++) {
var found = this[i].querySelectorAll(selector);
for (var j = 0; j < found.length; j++) {
foundElements.push(found[j]);
}
}
return new Dom7(foundElements);
},
children: function (selector) {
var children = [];
for (var i = 0; i < this.length; i++) {
var childNodes = this[i].childNodes;
for (var j = 0; j < childNodes.length; j++) {
if (!selector) {
if (childNodes[j].nodeType === 1) children.push(childNodes[j]);
}
else {
if (childNodes[j].nodeType === 1 && $(childNodes[j]).is(selector)) children.push(childNodes[j]);
}
}
}
return new Dom7($.unique(children));
},
remove: function () {
for (var i = 0; i < this.length; i++) {
if (this[i].parentNode) this[i].parentNode.removeChild(this[i]);
}
return this;
},
detach: function () {
return this.remove();
}
};
// Shortcuts
(function () {
var shortcuts = ('click blur focus focusin focusout keyup keydown keypress submit change mousedown mousemove mouseup mouseenter mouseleave mouseout mouseover touchstart touchend touchmove resize scroll').split(' ');
var notTrigger = ('resize scroll').split(' ');
function createMethod(name) {
Dom7.prototype[name] = function (handler) {
var i;
if (typeof handler === 'undefined') {
for (i = 0; i < this.length; i++) {
if (notTrigger.indexOf(name) < 0) this[i][name]();
}
return this;
}
else {
return this.on(name, handler);
}
};
}
for (var i = 0; i < shortcuts.length; i++) {
createMethod(shortcuts[i]);
}
})();
// Ajax
var _jsonpRequests = 0;
$.ajax = function (options) {
var defaults = {
method: 'GET',
data: false,
async: true,
cache: true,
user: '',
password: '',
headers: {},
xhrFields: {},
statusCode: {},
processData: true,
dataType: 'text',
contentType: 'application/x-www-form-urlencoded',
timeout: 0 // 0s JSONp timeout
};
//For jQuery guys
if (options.type) options.method = options.type;
// Merge options and defaults
for (var prop in defaults) {
if (!(prop in options)) options[prop] = defaults[prop];
}
// Default URL
if (!options.url) {
options.url = window.location.toString();
}
// UC method
var _method = options.method.toUpperCase();
// Data to modify GET URL
if ((_method === 'GET' || _method === 'HEAD') && options.data) {
var stringData;
if (typeof options.data === 'string') {
// Should be key=value string
if (options.data.indexOf('?') >= 0) stringData = options.data.split('?')[1];
else stringData = options.data;
}
else {
// Should be key=value object
stringData = $.serializeObject(options.data);
}
if (options.url.indexOf('?') >= 0) options.url += '&' + stringData;
else options.url += '?' + stringData;
}
// JSONP
if (options.dataType === 'json' && options.url.indexOf('callback=') >= 0) {
var callbackName = 'f7jsonp_' + Date.now() + (_jsonpRequests++);
var requestURL, abortTimeout;
var callbackSplit = options.url.split('callback=');
if (callbackSplit[1].indexOf('&') >= 0) {
var addVars = callbackSplit[1].split('&').filter(function (el) { return el.indexOf('=') > 0; }).join('&');
requestURL = callbackSplit[0] + 'callback=' + callbackName + (addVars.length > 0 ? '&' + addVars : '');
}
else {
requestURL = callbackSplit[0] + 'callback=' + callbackName;
}
// Create script
var script = document.createElement('script');
script.type = 'text/javascript';
script.onerror = function() {
clearTimeout(abortTimeout);
if (options.error) options.error();
};
script.src = requestURL;
// Handler
window[callbackName] = function (data) {
clearTimeout(abortTimeout);
if (options.success) options.success(data);
script.parentNode.removeChild(script);
script = null;
delete window[callbackName];
};
document.querySelector('head').appendChild(script);
if (options.timeout > 0) {
abortTimeout = setTimeout(function () {
script.parentNode.removeChild(script);
script = null;
if (options.error) options.error();
}, options.timeout);
}
return;
}
// Cache for GET/HEAD requests
if (_method === 'GET' || _method === 'HEAD') {
if (options.cache === false) options.url += ('_nocache=' + Date.now());
}
// Create XHR
var xhr = new XMLHttpRequest();
// Open XHR
xhr.open(_method, options.url, options.async, options.user, options.password);
// Create POST Data
var postData = null;
if ((_method === 'POST' || _method === 'PUT') && options.data) {
if (options.processData) {
var postDataInstances = [ArrayBuffer, Blob, Document, FormData];
// Post Data
if (postDataInstances.indexOf(options.data.constructor) >= 0) {
postData = options.data;
}
else {
// POST Headers
var boundary = '---------------------------' + Date.now().toString(16);
if (options.contentType === 'multipart\/form-data') {
xhr.setRequestHeader('Content-Type', 'multipart\/form-data; boundary=' + boundary);
}
else {
xhr.setRequestHeader('Content-Type', options.contentType);
}
postData = '';
var _data = $.serializeObject(options.data);
if (options.contentType === 'multipart\/form-data') {
boundary = '---------------------------' + Date.now().toString(16);
_data = _data.split('&');
var _newData = [];
for (var i = 0; i < _data.length; i++) {
_newData.push('Content-Disposition: form-data; name="' + _data[i].split('=')[0] + '"\r\n\r\n' + _data[i].split('=')[1] + '\r\n');
}
postData = '--' + boundary + '\r\n' + _newData.join('--' + boundary + '\r\n') + '--' + boundary + '--\r\n';
}
else {
postData = options.contentType === 'application/x-www-form-urlencoded' ? _data : _data.replace(/&/g, '\r\n');
}
}
}
else {
postData = options.data;
}
}
// Additional headers
if (options.headers) {
for (var header in options.headers) {
xhr.setRequestHeader(header, options.headers[header]);
}
}
// Check for crossDomain
if (typeof options.crossDomain === 'undefined') {
options.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(options.url) && RegExp.$2 !== window.location.host;
}
if (!options.crossDomain) {
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
}
if (options.xhrFields) {
for (var field in options.xhrFields) {
xhr[field] = options.xhrFields[field];
}
}
// Handle XHR
xhr.onload = function (e) {
if (xhr.status === 200 || xhr.status === 0) {
$(document).trigger('ajaxSuccess', {xhr: xhr});
if (options.success) {
var responseData = xhr.responseText;
if (options.dataType === 'json') responseData = JSON.parse(responseData);
options.success(responseData, xhr.status, xhr);
}
}
if (options.statusCode) {
if (options.statusCode[xhr.status]) options.statusCode[xhr.status](xhr);
}
if (options.complete) {
options.complete(xhr);
}
$(document).trigger('ajaxComplete', {xhr: xhr});
};
xhr.onerror = function (e) {
$(document).trigger('ajaxError', {xhr: xhr});
if (options.error) options.error(xhr);
};
// Ajax start callback
if (options.start) options.start(xhr);
// Send XHR
$(document).trigger('ajaxStart', {xhr: xhr});
xhr.send(postData);
// Return XHR object
return xhr;
};
// Shrotcuts
(function () {
var methods = ('get post getJSON').split(' ');
function createMethod(method) {
$[method] = function (url, data, success) {
return $.ajax({
url: url,
method: method === 'post' ? 'POST' : 'GET',
data: typeof data === 'function' ? undefined : data,
success: typeof data === 'function' ? data : success,
dataType: method === 'getJSON' ? 'json' : undefined
});
};
}
for (var i = 0; i < methods.length; i++) {
createMethod(methods[i]);
}
})();
// DOM Library Utilites
$.parseUrlQuery = function (url) {
var query = {}, i, params, param;
if (url.indexOf('?') >= 0) url = url.split('?')[1];
else return query;
params = url.split('&');
for (i = 0; i < params.length; i++) {
param = params[i].split('=');
query[param[0]] = param[1];
}
return query;
};
$.isArray = function (arr) {
if (Object.prototype.toString.apply(arr) === '[object Array]') return true;
else return false;
};
$.unique = function (arr) {
var unique = [];
for (var i = 0; i < arr.length; i++) {
if (unique.indexOf(arr[i]) === -1) unique.push(arr[i]);
}
return unique;
};
$.trim = function (str) {
return str.trim();
};
$.serializeObject = function (obj) {
if (typeof obj === 'string') return obj;
var resultArray = [];
var separator = '&';
for (var prop in obj) {
if ($.isArray(obj[prop])) {
var toPush = [];
for (var i = 0; i < obj[prop].length; i ++) {
toPush.push(prop + '=' + obj[prop][i]);
}
resultArray.push(toPush.join(separator));
}
else {
// Should be string
resultArray.push(prop + '=' + obj[prop]);
}
}
return resultArray.join(separator);
};
$.getTranslate = function (el, axis) {
var matrix, curTransform, curStyle, transformMatrix;
// automatic axis detection
if (typeof axis === 'undefined') {
axis = 'x';
}
curStyle = window.getComputedStyle(el, null);
if (window.WebKitCSSMatrix) {
// Some old versions of Webkit choke when 'none' is passed; pass
// empty string instead in this case
transformMatrix = new WebKitCSSMatrix(curStyle.webkitTransform === 'none' ? '' : curStyle.webkitTransform);
}
else {
transformMatrix = curStyle.MozTransform || curStyle.OTransform || curStyle.MsTransform || curStyle.msTransform || curStyle.transform || curStyle.getPropertyValue('transform').replace('translate(', 'matrix(1, 0, 0, 1,');
matrix = transformMatrix.toString().split(',');
}
if (axis === 'x') {
//Latest Chrome and webkits Fix
if (window.WebKitCSSMatrix)
curTransform = transformMatrix.m41;
//Crazy IE10 Matrix
else if (matrix.length === 16)
curTransform = parseFloat(matrix[12]);
//Normal Browsers
else
curTransform = parseFloat(matrix[4]);
}
if (axis === 'y') {
//Latest Chrome and webkits Fix
if (window.WebKitCSSMatrix)
curTransform = transformMatrix.m42;
//Crazy IE10 Matrix
else if (matrix.length === 16)
curTransform = parseFloat(matrix[13]);
//Normal Browsers
else
curTransform = parseFloat(matrix[5]);
}
return curTransform || 0;
};
$.requestAnimationFrame = function (callback) {
if (window.requestAnimationFrame) return window.requestAnimationFrame(callback);
else if (window.webkitRequestAnimationFrame) return window.webkitRequestAnimationFrame(callback);
else if (window.mozRequestAnimationFrame) return window.mozRequestAnimationFrame(callback);
else {
return window.setTimeout(callback, 1000 / 60);
}
};
$.supportTouch = !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch);
// Link to prototype
$.fn = Dom7.prototype;
// Plugins
$.fn.scrollTo = function (left, top, duration) {
return this.each(function () {
var el = this;
var currentTop, currentLeft, maxTop, maxLeft, newTop, newLeft, scrollTop, scrollLeft;
var animateTop = top > 0 || top === 0;
var animateLeft = left > 0 || left === 0;
if (animateTop) {
currentTop = el.scrollTop;
if (!duration) {
el.scrollTop = top;
}
}
if (animateLeft) {
currentLeft = el.scrollLeft;
if (!duration) {
el.scrollLeft = left;
}
}
if (!duration) return;
if (animateTop) {
maxTop = el.scrollHeight - el.offsetHeight;
newTop = Math.max(Math.min(top, maxTop), 0);
}
if (animateLeft) {
maxLeft = el.scrollWidth - el.offsetWidth;
newLeft = Math.max(Math.min(left, maxLeft), 0);
}
var startTime = null;
if (animateTop && newTop === currentTop) animateTop = false;
if (animateLeft && newLeft === currentLeft) animateLeft = false;
function render(time) {
if (time === undefined) {
time = new Date().getTime();
}
if (startTime === null) {
startTime = time;
}
var doneLeft, doneTop, done;
if (animateTop) scrollTop = currentTop + ((time - startTime) / duration * (newTop - currentTop));
if (animateLeft) scrollLeft = currentLeft + ((time - startTime) / duration * (newLeft - currentLeft));
if (animateTop && newTop > currentTop && scrollTop >= newTop) {
el.scrollTop = newTop;
done = true;
}
if (animateTop && newTop < currentTop && scrollTop <= newTop) {
el.scrollTop = newTop;
done = true;
}
if (animateLeft && newLeft > currentLeft && scrollLeft >= newLeft) {
el.scrollLeft = newLeft;
done = true;
}
if (animateLeft && newLeft < currentLeft && scrollLeft <= newLeft) {
el.scrollLeft = newLeft;
done = true;
}
if (done) return;
if (animateTop) el.scrollTop = scrollTop;
if (animateLeft) el.scrollLeft = scrollLeft;
$.requestAnimationFrame(render);
}
$.requestAnimationFrame(render);
});
};
$.fn.scrollTop = function (top, duration) {
var dom = this;
if (typeof top === 'undefined') {
if (dom.length > 0) return dom[0].scrollTop;
else return null;
}
return dom.scrollTo(undefined, top, duration);
};
$.fn.scrollLeft = function (left, duration) {
var dom = this;
if (typeof left === 'undefined') {
if (dom.length > 0) return dom[0].scrollLeft;
else return null;
}
return dom.scrollTo(left, undefined, duration);
};
return $;
})();
// Export Dom7 to Framework7
Framework7.$ = Dom7;
// Export to local scope
var $ = Dom7;
// Export to Window
window.Dom7 = Dom7;
/*===========================
Features Support Detection
===========================*/
Framework7.prototype.support = (function () {
var support = {
touch: !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch)
};
// Export object
return support;
})();
/*===========================
Device/OS Detection
===========================*/
Framework7.prototype.device = (function () {
var device = {};
var ua = navigator.userAgent;
var $ = Dom7;
var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
device.ios = device.android = device.iphone = device.ipad = false;
// Android
if (android) {
device.os = 'android';
device.osVersion = android[2];
device.android = true;
}
if (ipad || iphone || ipod) {
device.os = 'ios';
device.ios = true;
}
// iOS
if (iphone && !ipod) {
device.osVersion = iphone[2].replace(/_/g, '.');
device.iphone = true;
}
if (ipad) {
device.osVersion = ipad[2].replace(/_/g, '.');
device.ipad = true;
}
if (ipod) {
device.osVersion = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
device.iphone = true;
}
// iOS 8+ changed UA
if (device.ios && device.osVersion && ua.indexOf('Version/') >= 0) {
if (device.osVersion.split('.')[0] === '10') {
device.osVersion = ua.toLowerCase().split('version/')[1].split(' ')[0];
}
}
// Webview
device.webView = (iphone || ipad || ipod) && ua.match(/.*AppleWebKit(?!.*Safari)/i);
// Minimal UI
if (device.os && device.os === 'ios') {
var osVersionArr = device.osVersion.split('.');
device.minimalUi = !device.webView &&
(ipod || iphone) &&
(osVersionArr[0] * 1 === 7 ? osVersionArr[1] * 1 >= 1 : osVersionArr[0] * 1 > 7) &&
$('meta[name="viewport"]').length > 0 && $('meta[name="viewport"]').attr('content').indexOf('minimal-ui') >= 0;
}
// Check for status bar and fullscreen app mode
var windowWidth = $(window).width();
var windowHeight = $(window).height();
device.statusBar = false;
if (device.webView && (windowWidth * windowHeight === screen.width * screen.height)) {
device.statusBar = true;
}
else {
device.statusBar = false;
}
// Classes
var classNames = [];
// Pixel Ratio
device.pixelRatio = window.devicePixelRatio || 1;
if (device.pixelRatio >= 2) {
classNames.push('retina');
}
// OS classes
if (device.os) {
classNames.push(device.os, device.os + '-' + device.osVersion.split('.')[0], device.os + '-' + device.osVersion.replace(/\./g, '-'));
if (device.os === 'ios') {
var major = parseInt(device.osVersion.split('.')[0], 10);
for (var i = major - 1; i >= 6; i--) {
classNames.push('ios-gt-' + i);
}
}
}
// Status bar classes
if (device.statusBar) {
classNames.push('with-statusbar-overlay');
}
else {
$('html').removeClass('with-statusbar-overlay');
}
// Add html classes
if (classNames.length > 0) $('html').addClass(classNames.join(' '));
// Export object
return device;
})();
/*===========================
Plugins prototype
===========================*/
Framework7.prototype.plugins = {};
})();
/*===========================
Template7 Template engine
===========================*/
window.Template7 = (function () {
'use strict';
function isArray(arr) {
return Object.prototype.toString.apply(arr) === '[object Array]';
}
function isObject(obj) {
return obj instanceof Object;
}
function isFunction(func) {
return typeof func === 'function';
}
var cache = {};
function helperToSlices(string) {
var helperParts = string.replace(/[{}#}]/g, '').split(' ');
var slices = [];
var shiftIndex, i, j;
for (i = 0; i < helperParts.length; i++) {
var part = helperParts[i];
if (i === 0) slices.push(part);
else {
if (part.indexOf('"') === 0) {
// Plain String
if (part.match(/"/g).length === 2) {
// One word string
slices.push(part);
}
else {
// Find closed Index
shiftIndex = 0;
for (j = i + 1; j < helperParts.length; j++) {
part += ' ' + helperParts[j];
if (helperParts[j].indexOf('"') >= 0) {
shiftIndex = j;
slices.push(part);
break;
}
}
if (shiftIndex) i = shiftIndex;
}
}
else {
if (part.indexOf('=') > 0) {
// Hash
var hashParts = part.split('=');
var hashName = hashParts[0];
var hashContent = hashParts[1];
if (hashContent.match(/"/g).length !== 2) {
shiftIndex = 0;
for (j = i + 1; j < helperParts.length; j++) {
hashContent += ' ' + helperParts[j];
if (helperParts[j].indexOf('"') >= 0) {
shiftIndex = j;
break;
}
}
if (shiftIndex) i = shiftIndex;
}
var hash = [hashName, hashContent.replace(/"/g,'')];
slices.push(hash);
}
else {
// Plain variable
slices.push(part);
}
}
}
}
return slices;
}
function stringToBlocks(string) {
var blocks = [], i, j, k;
if (!string) return [];
var _blocks = string.split(/({{[^{^}]*}})/);
for (i = 0; i < _blocks.length; i++) {
var block = _blocks[i];
if (block === '') continue;
if (block.indexOf('{{') < 0) {
blocks.push({
type: 'plain',
content: block
});
}
else {
if (block.indexOf('{/') >= 0) {
continue;
}
if (block.indexOf('{#') < 0 && block.indexOf(' ') < 0 && block.indexOf('else') < 0) {
// Simple variable
blocks.push({
type: 'variable',
contextName: block.replace(/[{}]/g, '')
});
continue;
}
// Helpers
var helperSlices = helperToSlices(block);
var helperName = helperSlices[0];
var helperContext = [];
var helperHash = {};
for (j = 1; j < helperSlices.length; j++) {
var slice = helperSlices[j];
if (isArray(slice)) {
// Hash
helperHash[slice[0]] = slice[1] === 'false' ? false : slice[1];
}
else {
helperContext.push(slice);
}
}
if (block.indexOf('{#') >= 0) {
// Condition/Helper
var helperStartIndex = i;
var helperContent = '';
var elseContent = '';
var toSkip = 0;
var shiftIndex;
var foundClosed = false, foundElse = false, foundClosedElse = false, depth = 0;
for (j = i + 1; j < _blocks.length; j++) {
if (_blocks[j].indexOf('{{#') >= 0) {
depth ++;
}
if (_blocks[j].indexOf('{{/') >= 0) {
depth --;
}
if (_blocks[j].indexOf('{{#' + helperName) >= 0) {
helperContent += _blocks[j];
if (foundElse) elseContent += _blocks[j];
toSkip ++;
}
else if (_blocks[j].indexOf('{{/' + helperName) >= 0) {
if (toSkip > 0) {
toSkip--;
helperContent += _blocks[j];
if (foundElse) elseContent += _blocks[j];
}
else {
shiftIndex = j;
foundClosed = true;
break;
}
}
else if (_blocks[j].indexOf('else') >= 0 && depth === 0) {
foundElse = true;
}
else {
if (!foundElse) helperContent += _blocks[j];
if (foundElse) elseContent += _blocks[j];
}
}
if (foundClosed) {
if (shiftIndex) i = shiftIndex;
blocks.push({
type: 'helper',
helperName: helperName,
contextName: helperContext,
content: helperContent,
inverseContent: elseContent,
hash: helperHash
});
}
}
else if (block.indexOf(' ') > 0) {
blocks.push({
type: 'helper',
helperName: helperName,
contextName: helperContext,
hash: helperHash
});
}
}
}
return blocks;
}
var Template7 = function (template) {
var t = this;
t.template = template;
function getCompileFn(block, depth) {
if (block.content) return compile(block.content, depth);
else return function () {return ''; };
}
function getCompileInverse(block, depth) {
if (block.inverseContent) return compile(block.inverseContent, depth);
else return function () {return ''; };
}
function getCompileVar(name, ctx) {
var parents, variable, context;
if (name.indexOf('@global') >= 0) {
variable = '(Template7.global && Template7.global.' + (name.split('@global.')[1]) + ')';
}
else if (name.indexOf('@') >= 0) {
variable = '(data && data.' + name.replace('@', '') + ')';
}
else {
if (name.indexOf('.') > 0) {
if (name.indexOf('this') === 0) variable = name.replace('this', ctx);
else variable = ctx + '.' + name;
}
else if (name.indexOf('../') === 0) {
var levelUp = name.split('../').length - 1;
var newName = name.split('../')[name.split('../').length - 1];
var newDepth = ctx.split('_')[1] - levelUp;
variable = 'ctx_' + (newDepth >= 1 ? newDepth : 1) + '.' + newName;
}
else {
variable = name === 'this' ? ctx : ctx + '.' + name;
}
}
return variable;
}
function getCompiledArguments(contextArray, ctx) {
var arr = [];
for (var i = 0; i < contextArray.length; i++) {
if (contextArray[i].indexOf('"') === 0) arr.push(contextArray[i]);
else {
arr.push(getCompileVar(contextArray[i], ctx));
}
}
return arr.join(', ');
}
function compile(template, depth) {
depth = depth || 1;
template = template || t.template;
if (typeof template !== 'string') {
throw new Error('Template7: Template must be a string');
}
var blocks = stringToBlocks(template);
if (blocks.length === 0) {
return function () { return ''; };
}
var ctx = 'ctx_' + depth;
var resultString = '(function (' + ctx + ', data) {\n';
if (depth === 1) {
resultString += 'function isArray(arr){return Object.prototype.toString.apply(arr) === \'[object Array]\';}\n';
resultString += 'function isFunction(func){return (typeof func === \'function\');}\n';
resultString += 'function c(val, ctx) {if (typeof val !== "undefined") {if (isFunction(val)) {return val.call(ctx);} else return val;} else return "";}\n';
}
resultString += 'var r = \'\';\n';
var i, j, context;
for (i = 0; i < blocks.length; i++) {
var block = blocks[i];
// Plain block
if (block.type === 'plain') {
resultString += 'r +=\'' + (block.content).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/'/g, '\\' + '\'') + '\';';
continue;
}
var variable, compiledArguments;
// Variable block
if (block.type === 'variable') {
variable = getCompileVar(block.contextName, ctx);
resultString += 'r += c(' + variable + ', ' + ctx + ');';
}
// Helpers block
if (block.type === 'helper') {
if (block.helperName in t.helpers) {
compiledArguments = getCompiledArguments(block.contextName, ctx);
resultString += 'r += (Template7.helpers.' + block.helperName + ').call(' + ctx + ', ' + (compiledArguments && (compiledArguments + ',')) +'{hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + '});';
}
else {
if (block.contextName.length > 0) {
throw new Error('Template7: Missing helper: "' + block.helperName + '"');
}
else {
variable = getCompileVar(block.helperName, ctx);
resultString += 'if (' + variable + ') {';
resultString += 'if (isArray(' + variable + ')) {';
resultString += 'r += (Template7.helpers.each).call(' + ctx + ', ' + variable + ', {hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + '});';
resultString += '}else {';
resultString += 'r += (Template7.helpers.with).call(' + ctx + ', ' + variable + ', {hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + '});';
resultString += '}}';
}
}
}
}
resultString += '\nreturn r;})';
return eval.call(window, resultString);
}
t.compile = function (template) {
if (!t.compiled) {
t.compiled = compile(template);
}
return t.compiled;
};
};
Template7.prototype = {
options: {},
helpers: {
'if': function (context, options) {
if (isFunction(context)) { context = context.call(this); }
if (context) {
return options.fn(this, options.data);
}
else {
return options.inverse(this, options.data);
}
},
'unless': function (context, options) {
if (isFunction(context)) { context = context.call(this); }
if (!context) {
return options.fn(this, options.data);
}
else {
return options.inverse(this, options.data);
}
},
'each': function (context, options) {
var ret = '', i = 0;
if (isFunction(context)) { context = context.call(this); }
if (isArray(context)) {
if (options.hash.reverse) {
context = context.reverse();
}
for (i = 0; i < context.length; i++) {
ret += options.fn(context[i], {first: i === 0, last: i === context.length - 1, index: i});
}
if (options.hash.reverse) {
context = context.reverse();
}
}
else {
for (var key in context) {
i++;
ret += options.fn(context[key], {key: key});
}
}
if (i > 0) return ret;
else return options.inverse(this);
},
'with': function (context, options) {
if (isFunction(context)) { context = context.call(this); }
return options.fn(context);
},
'join': function (context, options) {
if (isFunction(context)) { context = context.call(this); }
return context.join(options.hash.delimiter || options.hash.delimeter);
}
}
};
var t7 = function (template, data) {
if (arguments.length === 2) {
var instance = new Template7(template);
var rendered = instance.compile()(data);
instance = null;
return (rendered);
}
else return new Template7(template);
};
t7.registerHelper = function (name, fn) {
Template7.prototype.helpers[name] = fn;
};
t7.unregisterHelper = function (name) {
Template7.prototype.helpers[name] = undefined;
delete Template7.prototype.helpers[name];
};
t7.compile = function (template, options) {
var instance = new Template7(template, options);
return instance.compile();
};
t7.options = Template7.prototype.options;
t7.helpers = Template7.prototype.helpers;
return t7;
})();
//# sourceMappingURL=framework7.js.map