├── .gitignore ├── Gruntfile.js ├── LICENSE.txt ├── README.md ├── bower.json ├── demo.html ├── package.json └── src ├── ionic-scroller.js └── ionic-scroller.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | bower_components 3 | .DS_Store 4 | node_modules 5 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | uglify: { 7 | my_target: { 8 | files: { 9 | "src/ionic-scroller.min.js": "src/ionic-scroller.js" 10 | } 11 | } 12 | } 13 | }); 14 | 15 | grunt.loadNpmTasks('grunt-contrib-uglify'); 16 | 17 | grunt.registerTask('default', ['uglify']); 18 | }; 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Bengt Weiße other contributors 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ionicScroller 2 | [](https://nodei.co/npm/ionic-scroller/) 3 | 4 | ionicScroller is an [Angular.js](http://angularjs.org/) and [Ionic Framework](http://ionicframework.com/) extension for simple scroll-top, -bottom, -to functionality. 5 | 6 | Installation 7 | ============ 8 | - run `bower install ionic-scroller` 9 | - or run `npm install ionic-scroller` 10 | - or download zip from release page: https://github.com/KillerCodeMonkey/ionicScroller/releases 11 | 12 | Usage 13 | ===== 14 | - load ionic, ionic-scroller scripts in your index.html 15 | - add dependency to your app module `var myAppModule = angular.module('ionicScrollerTest', ['ionic', 'ionicScroller']);` 16 | - use directives in your html --> ion-scroller-top, ion-scroller-bottom, ion-scroller-to, ion-scroller-anchor 17 | - `` 18 | - the generated buttons are hidden on shown with ng-show/ng-hide, so you can animate showing and hiding of the buttons through css as well ;) 19 | - add class with absolute position to move button over your list ;) 20 | 21 | Configuration 22 | ============= 23 | ### ion-scroller-top and ion-scroller-bottom 24 | - change button text: `text="YOUR TEXT OR EMPTY"` (default: Top / Bottom) 25 | - animate scrolling: `animate="true"` (default false) 26 | - auto-hide at offset: `offset="10"` (ion-scroller-bottom: hides button if scroll position top >= offset, default: -1; ion-scroller-top: hides button if scroll position top <= offset, default: -1; offset="-1": does not hide the button) 27 | - connect to delegateHandle: `scroll-delegate="delegateHandle"` (default, the main delegateHandle like ion-content) 28 | - style through class or ng-class expression: `css-class="button button-assertive"` (wraps up in ng-class, default: 'button button-dark') 29 | 30 | ### ion-scroller-to 31 | - change button text: `text="YOUR TEXT OR EMPTY"` (default: GoTo) 32 | - animate scrolling: `animate="true"` (default false) 33 | - left, top: `left="10" top="50"` (target scroll position, required) 34 | - connect to delegateHandle: `scroll-delegate="delegateHandle"` (default, the main delegateHandle like ion-content) 35 | - style through class or ng-class expression: `css-class="button button-assertive"` (wraps up in ng-class, default: 'button button-dark') 36 | 37 | ### ion-scroller-anchor 38 | - change button text: `text="YOUR TEXT OR EMPTY"` (default: GoTo) 39 | - animate scrolling: `animate="true"` (default false) 40 | - anchor: `anchor="domNodeId"` (target node-id, required) 41 | - connect to delegateHandle: `scroll-delegate="delegateHandle"` (default, the main delegateHandle like ion-content) 42 | - style through class or ng-class expression: `css-class="button button-assertive"` (wraps up in ng-class, default: 'button button-dark') 43 | 44 | 45 | [](https://bitdeli.com/free "Bitdeli Badge") 46 | 47 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ionic-scroller", 3 | "version": "0.0.4", 4 | "authors": [ 5 | "Bengt Weiße " 6 | ], 7 | "description": "Angular and IonicFramework extension to scroll-top, -bottom, -to, -anchor", 8 | "main": ["src/ionic-scroller.min.js"], 9 | "ignore": [ 10 | "bower_components", 11 | "node_modules", 12 | "spec", 13 | "demo.html", 14 | "package.json", 15 | "Gruntfile.js" 16 | ], 17 | "dependencies": { 18 | "ionic": "~1.0.1" 19 | }, 20 | "keywords": [ 21 | "scroll", 22 | "scrolling", 23 | "scroller", 24 | "ionic framework", 25 | "ionic", 26 | "angular", 27 | "directive" 28 | ], 29 | "license": "MIT" 30 | } 31 | -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Ionic Scroller Test 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {{$index}} - This is a basic Card which contains an item that has wrapping text. 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ionic-scroller", 3 | "version": "0.0.4", 4 | "description": "Angular and IonicFramework extension to scroll-top, -bottom, -to, -anchor", 5 | "author": "Bengt Weiße ", 6 | "homepage": "https://github.com/KillerCodeMonkey/ionicScroller", 7 | "main": "src/ionic-scroller.min.js", 8 | "devDependencies": { 9 | "grunt": "~0.4.3", 10 | "grunt-contrib-uglify": "~0.6.0" 11 | }, 12 | "engines": { 13 | "node": ">=0.10" 14 | }, 15 | "license": "MIT", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/KillerCodeMonkey/ionicScroller" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/KillerCodeMonkey/ionicScroller/issues" 22 | }, 23 | "keywords": [ 24 | "scroll", 25 | "scrolling", 26 | "scroller", 27 | "ionic framework", 28 | "ionic", 29 | "angular", 30 | "directive" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /src/ionic-scroller.js: -------------------------------------------------------------------------------- 1 | /* global ionic */ 2 | (function () { 3 | 'use strict'; 4 | var app; 5 | // declare ionicScroller module 6 | app = angular.module('ionicScroller', ['ionic']); 7 | 8 | app.directive('ionScrollerTop', [ 9 | '$timeout', 10 | '$ionicScrollDelegate', 11 | function ($timeout, $ionicScrollDelegate) { 12 | return { 13 | scope: { 14 | 'cssClass': '@?', 15 | 'offset': '@?', 16 | 'text': '@?', 17 | 'animate': '@?', 18 | 'scrollDelegate': '@?' 19 | }, 20 | restrict: 'E', 21 | replace: true, 22 | template: '{{text}}', 23 | link: function ($scope) { 24 | var defaults = { 25 | 'cssClass': 'button button-dark', 26 | 'offset': -1, 27 | 'text': 'Top' 28 | }, 29 | showAlways, 30 | delegateHandle, 31 | setVisible = function() { 32 | if (showAlways) { 33 | $scope.show = true; 34 | return; 35 | } 36 | $scope.show = delegateHandle.getScrollPosition().top >= $scope.offset; 37 | }; 38 | 39 | $scope.cssClass = $scope.cssClass || defaults.cssClass; 40 | $scope.animate = $scope.animate === null || $scope.animate === undefined || $scope.animate === '' || $scope.animate === 'false' ? false : true; 41 | $scope.offset = $scope.offset === null || $scope.offset === undefined || $scope.offset === '' ? defaults.offset : $scope.offset; 42 | $scope.text = $scope.text === null || $scope.text === undefined ? defaults.text : $scope.text; 43 | 44 | if ($scope.scrollDelegate && $ionicScrollDelegate.$getByHandle($scope.scrollDelegate)) { 45 | delegateHandle = $ionicScrollDelegate.$getByHandle($scope.scrollDelegate); 46 | } else { 47 | delegateHandle = $ionicScrollDelegate; 48 | } 49 | 50 | showAlways = $scope.offset === -1; 51 | 52 | if (delegateHandle) { 53 | setVisible(); 54 | 55 | if (!showAlways) { 56 | ionic.EventController.on('scroll', function () { 57 | $timeout(setVisible); 58 | }, delegateHandle.getScrollView().__container); 59 | } 60 | 61 | $scope.scrollTop = function () { 62 | delegateHandle.scrollTop($scope.animate); 63 | }; 64 | } 65 | } 66 | }; 67 | } 68 | ]); 69 | 70 | app.directive('ionScrollerBottom', [ 71 | '$timeout', 72 | '$ionicScrollDelegate', 73 | function ($timeout, $ionicScrollDelegate) { 74 | return { 75 | scope: { 76 | 'cssClass': '@?', 77 | 'offset': '@?', 78 | 'text': '@?', 79 | 'animate': '@?', 80 | 'scrollDelegate': '@?' 81 | }, 82 | restrict: 'E', 83 | replace: true, 84 | template: '{{text}}', 85 | link: function ($scope) { 86 | var defaults = { 87 | 'cssClass': 'button button-dark', 88 | 'offset': -1, 89 | 'text': 'Bottom' 90 | }, 91 | showAlways, 92 | scrollContainer, 93 | delegateHandle, 94 | setVisible = function() { 95 | if (showAlways) { 96 | $scope.show = true; 97 | return; 98 | } 99 | $scope.show = delegateHandle.getScrollPosition().top <= $scope.offset; 100 | }; 101 | 102 | $scope.cssClass = $scope.cssClass || defaults.cssClass; 103 | $scope.animate = $scope.animate === null || $scope.animate === undefined || $scope.animate === '' || $scope.animate === 'false' ? false : true; 104 | $scope.offset = $scope.offset === null || $scope.offset === undefined || $scope.offset === '' ? defaults.offset : parseInt($scope.offset, 10); 105 | $scope.text = $scope.text === null || $scope.text === undefined ? defaults.text : $scope.text; 106 | 107 | if ($scope.scrollDelegate && $ionicScrollDelegate.$getByHandle($scope.scrollDelegate)) { 108 | delegateHandle = $ionicScrollDelegate.$getByHandle($scope.scrollDelegate); 109 | } else { 110 | delegateHandle = $ionicScrollDelegate; 111 | } 112 | 113 | showAlways = $scope.offset === -1; 114 | 115 | if (delegateHandle) { 116 | scrollContainer = delegateHandle.getScrollView().__container; 117 | setVisible(); 118 | 119 | if (!showAlways) { 120 | ionic.EventController.on('scroll', function () { 121 | $timeout(setVisible); 122 | }, scrollContainer); 123 | } 124 | 125 | $scope.scrollBottom = function () { 126 | delegateHandle.scrollBottom($scope.animate); 127 | }; 128 | } 129 | } 130 | }; 131 | } 132 | ]); 133 | 134 | app.directive('ionScrollerTo', [ 135 | '$ionicScrollDelegate', 136 | function ($ionicScrollDelegate) { 137 | return { 138 | scope: { 139 | 'cssClass': '@?', 140 | 'left': '=', 141 | 'top': '=', 142 | 'text': '@?', 143 | 'animate': '@?', 144 | 'scrollDelegate': '@?' 145 | }, 146 | restrict: 'E', 147 | replace: true, 148 | template: '{{text}}', 149 | link: function ($scope) { 150 | var defaults = { 151 | 'cssClass': 'button button-dark', 152 | 'text': 'GoTo' 153 | }, 154 | delegateHandle; 155 | 156 | $scope.cssClass = $scope.cssClass || defaults.cssClass; 157 | $scope.animate = $scope.animate === null || $scope.animate === undefined || $scope.animate === '' || $scope.animate === 'false' ? false : true; 158 | $scope.text = $scope.text === null || $scope.text === undefined ? defaults.text : $scope.text; 159 | 160 | if ($scope.scrollDelegate && $ionicScrollDelegate.$getByHandle($scope.scrollDelegate)) { 161 | delegateHandle = $ionicScrollDelegate.$getByHandle($scope.scrollDelegate); 162 | } else { 163 | delegateHandle = $ionicScrollDelegate; 164 | } 165 | 166 | if (delegateHandle) { 167 | $scope.scrollTo = function () { 168 | delegateHandle.scrollTo($scope.left, $scope.top, $scope.animate); 169 | }; 170 | } 171 | } 172 | }; 173 | } 174 | ]); 175 | 176 | app.directive('ionScrollerAnchor', [ 177 | '$location', 178 | '$ionicScrollDelegate', 179 | function ($location, $ionicScrollDelegate) { 180 | return { 181 | scope: { 182 | 'cssClass': '@?', 183 | 'anchor': '@', 184 | 'text': '@?', 185 | 'animate': '@?', 186 | 'scrollDelegate': '@?' 187 | }, 188 | restrict: 'E', 189 | replace: true, 190 | template: '{{text}}', 191 | link: function ($scope) { 192 | var defaults = { 193 | 'cssClass': 'button button-dark', 194 | 'text': 'GoTo' 195 | }, 196 | delegateHandle; 197 | 198 | $scope.cssClass = $scope.cssClass || defaults.cssClass; 199 | $scope.animate = $scope.animate === null || $scope.animate === undefined || $scope.animate === '' || $scope.animate === 'false' ? false : true; 200 | $scope.text = $scope.text === null || $scope.text === undefined ? defaults.text : $scope.text; 201 | 202 | if ($scope.scrollDelegate && $ionicScrollDelegate.$getByHandle($scope.scrollDelegate)) { 203 | delegateHandle = $ionicScrollDelegate.$getByHandle($scope.scrollDelegate); 204 | } else { 205 | delegateHandle = $ionicScrollDelegate; 206 | } 207 | 208 | if (delegateHandle) { 209 | $scope.scrollAnchor = function () { 210 | $location.hash($scope.anchor); 211 | delegateHandle.anchorScroll($scope.animate); 212 | }; 213 | } 214 | } 215 | }; 216 | } 217 | ]); 218 | }).call(this); 219 | -------------------------------------------------------------------------------- /src/ionic-scroller.min.js: -------------------------------------------------------------------------------- 1 | (function(){"use strict";var a;a=angular.module("ionicScroller",["ionic"]),a.directive("ionScrollerTop",["$timeout","$ionicScrollDelegate",function(a,b){return{scope:{cssClass:"@?",offset:"@?",text:"@?",animate:"@?",scrollDelegate:"@?"},restrict:"E",replace:!0,template:'{{text}}',link:function(c){var d,e,f={cssClass:"button button-dark",offset:-1,text:"Top"},g=function(){return d?void(c.show=!0):void(c.show=e.getScrollPosition().top>=c.offset)};c.cssClass=c.cssClass||f.cssClass,c.animate=null===c.animate||void 0===c.animate||""===c.animate||"false"===c.animate?!1:!0,c.offset=null===c.offset||void 0===c.offset||""===c.offset?f.offset:c.offset,c.text=null===c.text||void 0===c.text?f.text:c.text,e=c.scrollDelegate&&b.$getByHandle(c.scrollDelegate)?b.$getByHandle(c.scrollDelegate):b,d=-1===c.offset,e&&(g(),d||ionic.EventController.on("scroll",function(){a(g)},e.getScrollView().__container),c.scrollTop=function(){e.scrollTop(c.animate)})}}}]),a.directive("ionScrollerBottom",["$timeout","$ionicScrollDelegate",function(a,b){return{scope:{cssClass:"@?",offset:"@?",text:"@?",animate:"@?",scrollDelegate:"@?"},restrict:"E",replace:!0,template:'{{text}}',link:function(c){var d,e,f,g={cssClass:"button button-dark",offset:-1,text:"Bottom"},h=function(){return d?void(c.show=!0):void(c.show=f.getScrollPosition().top<=c.offset)};c.cssClass=c.cssClass||g.cssClass,c.animate=null===c.animate||void 0===c.animate||""===c.animate||"false"===c.animate?!1:!0,c.offset=null===c.offset||void 0===c.offset||""===c.offset?g.offset:parseInt(c.offset,10),c.text=null===c.text||void 0===c.text?g.text:c.text,f=c.scrollDelegate&&b.$getByHandle(c.scrollDelegate)?b.$getByHandle(c.scrollDelegate):b,d=-1===c.offset,f&&(e=f.getScrollView().__container,h(),d||ionic.EventController.on("scroll",function(){a(h)},e),c.scrollBottom=function(){f.scrollBottom(c.animate)})}}}]),a.directive("ionScrollerTo",["$ionicScrollDelegate",function(a){return{scope:{cssClass:"@?",left:"=",top:"=",text:"@?",animate:"@?",scrollDelegate:"@?"},restrict:"E",replace:!0,template:'{{text}}',link:function(b){var c,d={cssClass:"button button-dark",text:"GoTo"};b.cssClass=b.cssClass||d.cssClass,b.animate=null===b.animate||void 0===b.animate||""===b.animate||"false"===b.animate?!1:!0,b.text=null===b.text||void 0===b.text?d.text:b.text,c=b.scrollDelegate&&a.$getByHandle(b.scrollDelegate)?a.$getByHandle(b.scrollDelegate):a,c&&(b.scrollTo=function(){c.scrollTo(b.left,b.top,b.animate)})}}}]),a.directive("ionScrollerAnchor",["$location","$ionicScrollDelegate",function(a,b){return{scope:{cssClass:"@?",anchor:"@",text:"@?",animate:"@?",scrollDelegate:"@?"},restrict:"E",replace:!0,template:'{{text}}',link:function(c){var d,e={cssClass:"button button-dark",text:"GoTo"};c.cssClass=c.cssClass||e.cssClass,c.animate=null===c.animate||void 0===c.animate||""===c.animate||"false"===c.animate?!1:!0,c.text=null===c.text||void 0===c.text?e.text:c.text,d=c.scrollDelegate&&b.$getByHandle(c.scrollDelegate)?b.$getByHandle(c.scrollDelegate):b,d&&(c.scrollAnchor=function(){a.hash(c.anchor),d.anchorScroll(c.animate)})}}}])}).call(this); --------------------------------------------------------------------------------