summaryrefslogtreecommitdiff
path: root/www/external/ionic.scroll.sista.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/external/ionic.scroll.sista.js')
-rw-r--r--www/external/ionic.scroll.sista.js303
1 files changed, 303 insertions, 0 deletions
diff --git a/www/external/ionic.scroll.sista.js b/www/external/ionic.scroll.sista.js
new file mode 100644
index 00000000..63711ff9
--- /dev/null
+++ b/www/external/ionic.scroll.sista.js
@@ -0,0 +1,303 @@
+/* global angular,ionic */
+(function (angular, ionic) {
+ 'use strict';
+
+ angular.module('jett.ionic.scroll.sista', ['ionic'])
+ .directive('scrollSista', ['$document', '$timeout', '$rootScope', '$ionicScrollDelegate', function($document, $timeout, $rootScope, $ionicScrollDelegate) {
+ var TRANSITION_DELAY = 400;
+ var defaultDelay = TRANSITION_DELAY * 2;
+ var defaultDuration = TRANSITION_DELAY + 'ms';
+ var scaleHeaderElements = ionic.Platform.isAndroid() ? false : true;
+
+ function getParentWithAttr (e, attrName, attrValue, depth) {
+ var attr;
+
+ depth = depth || 10;
+ while (e.parentNode && depth--) {
+ attr = e.parentNode.getAttribute(attrName);
+ if (attr && attr === attrValue) {
+ return e.parentNode;
+ }
+ e = e.parentNode;
+ }
+ return null;
+ }
+
+ return {
+ restrict: 'A',
+ link: function($scope, $element, $attr) {
+ var isNavBarTransitioning = true;
+ var body = $document[0].body;
+ var scrollDelegate = $attr.delegateHandle ? $ionicScrollDelegate.$getByHandle($attr.delegateHandle) : $ionicScrollDelegate;
+ var scrollView = scrollDelegate.getScrollView();
+
+ //coordinates
+ var y, prevY, prevScrollTop;
+ //headers
+ var cachedHeader, activeHeader, headerHeight, contentTop;
+ //subheader
+ var subHeader, subHeaderHeight;
+ //tabs
+ var tabs, tabsHeight, hasTabsTop = false, hasTabsBottom = false;
+
+ //y position that will indicate where specific elements should start and end their transition.
+ var headerStart = 0;
+ var tabsStart = 0;
+ var subheaderStart = 0;
+ var defaultEnd, headerEnd, tabsEnd, subheaderEnd;
+
+ /**
+ * translates an element along the y axis by the supplied value. if duration is passed in,
+ * a transition duration is set
+ * @param element
+ * @param y
+ * @param duration
+ */
+ function translateY (element, y, duration) {
+ if (duration && !element.style[ionic.CSS.TRANSITION_DURATION]) {
+ element.style[ionic.CSS.TRANSITION_DURATION] = duration;
+ $timeout(function () {
+ element.style[ionic.CSS.TRANSITION_DURATION] = '';
+ }, defaultDelay, false);
+ }
+ element.style[ionic.CSS.TRANSFORM] = 'translate3d(0, ' + (-y) + 'px, 0)';
+ }
+
+ /**
+ * Initializes y and scroll variables
+ */
+ function initCoordinates () {
+ y = 0;
+ prevY = 0;
+ prevScrollTop = 0;
+ }
+
+ /**
+ * Initializes headers, tabs, and subheaders, and determines how they will transition on scroll
+ */
+ function init () {
+ var activeView;
+
+ cachedHeader = body.querySelector('[nav-bar="cached"] .bar-header');
+ activeHeader = body.querySelector('[nav-bar="active"] .bar-header');
+
+ if (!activeHeader) {
+ return;
+ }
+
+ headerHeight = activeHeader.offsetHeight;
+ contentTop = headerHeight;
+
+ //since some people can have nested tabs, get the last tabs
+ tabs = body.querySelectorAll('.tabs');
+ tabs = tabs[tabs.length - 1];
+ if (tabs) {
+ tabsHeight = tabs.offsetHeight;
+ if (tabs.parentNode.classList.contains('tabs-top')) {
+ hasTabsTop = true;
+ contentTop += tabsHeight;
+ } else if (tabs.parentNode.classList.contains('tabs-bottom')) {
+ hasTabsBottom = true;
+ }
+ }
+
+ //subheader
+ //since subheader is going to be nested in the active view, get the closest active view from $element and
+ activeView = getParentWithAttr($element[0], 'nav-view', 'active');
+ subHeader = activeView && activeView.querySelector('.bar-subheader');
+ if (subHeader) {
+ subHeaderHeight = subHeader.offsetHeight;
+ contentTop += subHeaderHeight;
+ }
+
+ //set default end for header/tabs elements to scroll out of the scroll view and set elements end to default
+ defaultEnd = contentTop * 2;
+ headerEnd = tabsEnd = subheaderEnd = defaultEnd;
+
+ //if tabs or subheader aren't available, set height to 0
+ tabsHeight = tabsHeight || 0;
+ subHeaderHeight = subHeaderHeight || 0;
+
+ switch($attr.scrollSista) {
+ case 'header':
+ subheaderEnd = headerHeight;
+ tabsEnd = hasTabsTop ? headerHeight : 0;
+ break;
+ case 'header-tabs':
+ headerStart = hasTabsTop ? tabsHeight : 0;
+ subheaderEnd = hasTabsTop ? headerHeight + tabsHeight : headerHeight;
+ break;
+ case 'tabs-subheader':
+ headerEnd = 0;
+ headerStart = hasTabsTop ? contentTop - headerHeight : subHeaderHeight;
+ tabsStart = hasTabsTop ? subHeaderHeight : 0;
+ break;
+ case 'tabs':
+ headerEnd = 0;
+ subheaderEnd = hasTabsTop ? tabsHeight : 0;
+ break;
+ case 'subheader':
+ headerEnd = 0;
+ tabsEnd = 0;
+ break;
+ case 'header-subheader':
+ tabsEnd = hasTabsTop ? headerHeight : 0;
+ break;
+ case 'subheader-header':
+ headerStart = subHeaderHeight;
+ tabsStart = hasTabsTop ? subHeaderHeight : 0;
+ tabsEnd = hasTabsTop ? headerHeight : 0;
+ break;
+ //defaults to header-tabs-subheader
+ default:
+ headerStart = hasTabsTop ? contentTop - headerHeight : subHeaderHeight;
+ tabsStart = hasTabsTop ? subHeaderHeight : 0;
+ }
+ }
+
+ /**
+ * Translates active and cached headers, and animates active children
+ * @param y
+ * @param duration
+ */
+ function translateHeaders (y, duration) {
+ var fadeAmt = Math.max(0, 1 - (y / headerHeight));
+
+ //translate active header
+ if (activeHeader) {
+ translateY(activeHeader, y, duration);
+ angular.forEach(activeHeader.children, function (child) {
+ child.style.opacity = fadeAmt;
+ if (scaleHeaderElements) {
+ child.style[ionic.CSS.TRANSFORM] = 'scale(' + fadeAmt + ',' + fadeAmt + ')';
+ }
+ });
+ }
+
+ //translate cached header
+ if (cachedHeader) {
+ translateY(cachedHeader, y, duration);
+ }
+ }
+
+ /**
+ * Translates header, tabs, subheader elements and resets content top and/or bottom
+ * When the active view leaves, we need sync functionality to reset headers and clear
+ * @param y
+ * @param duration
+ */
+ function translateElementsSync (y, duration) {
+ var contentStyle = $element[0].style;
+ var headerY = y > headerStart ? y - headerStart : 0;
+ var tabsY, subheaderY;
+
+ //subheader
+ if (subHeader) {
+ subheaderY = y > subheaderStart ? y - subheaderStart : 0;
+ translateY(subHeader, Math.min(subheaderEnd, subheaderY), duration);
+ }
+
+ //tabs
+ if (tabs) {
+ tabsY = Math.min(tabsEnd, y > tabsStart ? y - tabsStart : 0);
+
+ if (hasTabsBottom) {
+ tabsY = -tabsY;
+ contentStyle.bottom = Math.max(0, tabsHeight - y) + 'px';
+ }
+ translateY(tabs, tabsY, duration);
+ }
+
+ //headers
+ translateHeaders(Math.min(headerEnd, headerY), duration);
+
+ //readjust top of ion-content
+ contentStyle.top = Math.max(0, contentTop - y) + 'px';
+ }
+
+ /**
+ * Translates header, tabs, subheader elements and resets content top and/or bottom
+ * Wraps translate functionality in an animation frame request
+ * @param y
+ * @param duration
+ */
+ function translateElements (y, duration) {
+ ionic.requestAnimationFrame(function() {
+ translateElementsSync(y, duration);
+ });
+ }
+
+ //Need to reinitialize the values on refreshComplete or things will get out of wack
+ $scope.$on('scroll.refreshComplete', function () {
+ initCoordinates();
+ });
+
+ /**
+ * Before the active view leaves, reset elements, and reset the scroll container
+ */
+ $scope.$parent.$on('$ionicView.beforeLeave', function () {
+ isNavBarTransitioning = true;
+ translateElementsSync(0);
+ activeHeader = null;
+ cachedHeader = null;
+ });
+
+ /**
+ * Scroll to the top when entering to reset then scrollView scrollTop. (prevents jumping)
+ */
+ $scope.$parent.$on('$ionicView.beforeEnter', function () {
+ if (scrollView) {
+ scrollView.scrollTo(0, 0);
+ }
+ });
+
+ /**
+ * Ionic sets the active/cached nav-bar AFTER the afterEnter event is called, so we need to set a small
+ * timeout to let the nav-bar logic run.
+ */
+ $scope.$parent.$on('$ionicView.afterEnter', function () {
+ initCoordinates();
+
+ $timeout(function () {
+ init();
+ isNavBarTransitioning = false;
+ }, 20, false);
+ });
+
+ /**
+ * Called onScroll. computes coordinates based on scroll position and translates accordingly
+ */
+ $element.bind('scroll', function (e) {
+ if (isNavBarTransitioning) {
+ return;
+ }
+ //support for jQuery event as well
+ e = e.originalEvent || e;
+
+ var duration = 0;
+ var scrollTop = e.detail?e.detail.scrollTop:e.target.scrollTop;
+
+ y = scrollTop >= 0 ? Math.min(defaultEnd, Math.max(0, y + scrollTop - prevScrollTop)) : 0;
+
+ //if we are at the bottom, animate the header/tabs back in
+ if (scrollView.getScrollMax().top - scrollTop <= contentTop) {
+ y = 0;
+ duration = defaultDuration;
+ }
+
+ prevScrollTop = scrollTop;
+
+ //if previous and current y are the same, no need to continue
+ if (prevY === y) {
+ return;
+ }
+ prevY = y;
+
+ translateElements(y, duration);
+ });
+
+ }
+ }
+ }]);
+
+})(angular, ionic);