├── .editorconfig ├── .gitignore ├── .jshintrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bower.json ├── build ├── angular-scroll-watch.js └── angular-scroll-watch.min.js ├── example ├── README.md ├── assets │ ├── digest-count.js │ ├── edit-on-plunker.js │ └── lesshat-prefixed.less ├── digest-sync │ ├── index.html │ ├── style.css │ └── style.less ├── infinite-scroll │ ├── index.html │ ├── style.css │ └── style.less ├── locals │ ├── index.html │ ├── style.css │ └── style.less ├── sw-broadcast-through-emit │ ├── index.html │ ├── style.css │ └── style.less ├── sw-broadcast │ ├── index.html │ ├── style.css │ └── style.less ├── sw-class-with-ng-repeat │ ├── index.html │ ├── style.css │ └── style.less ├── sw-class │ ├── index.html │ ├── style.css │ └── style.less ├── sw-stage │ ├── index.html │ ├── style.css │ └── style.less ├── sw-style-touch │ ├── index.html │ ├── style.css │ └── style.less └── sw-style │ ├── index.html │ ├── style.css │ └── style.less ├── gulpfile.js ├── images ├── how_much_angular_do_you_want.gif └── lovely_slides.gif ├── index.html ├── package.json └── src └── angular-scroll-watch.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | insert_final_newline = true 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | *.sublime-* 4 | .tern-port 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "undef": true, 3 | "predef": ["angular"] 4 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v0.6.1 4 | 5 | - Fix package name typo 6 | 7 | ## v0.6.0 8 | 9 | - Publish to NPM ([#8](https://github.com/pc035860/angular-scroll-watch/issues/8)) 10 | - Support browserify/webpack/requirejs with UMD 11 | 12 | ## v0.5.5 13 | 14 | - Fix `sw-stage` destroy issue ([#6](https://github.com/pc035860/angular-scroll-watch/issues/6)) 15 | 16 | ## v0.5.4 17 | 18 | - Update bower main path ([#3](https://github.com/pc035860/angular-scroll-watch/pull/3)) 19 | 20 | ## v0.5.3 21 | 22 | - Support AngularJS 1.3 class animations ([#2](https://github.com/pc035860/angular-scroll-watch/issues/2)) 23 | 24 | ## v0.5.2 25 | 26 | - Prevent inifinite digest on `scroll-watch` 27 | 28 | ## v0.5.1 29 | 30 | - Fix `sw-stage` annotation error 31 | 32 | ## v0.5.0 33 | 34 | - New directive: `sw-stage` 35 | - Support more locals: `$height`, `$offsetTop`, `$stageTop` 36 | 37 | ## v0.4.0 38 | 39 | - Rename the **digest hook** config to `digestSync` 40 | 41 | ## v0.3.0 42 | 43 | - Introduce **digest hook** as `digest` in `scroll-watch` configs 44 | 45 | ## v0.2.0 46 | 47 | - New directive attribute: `sw-broadcast` 48 | 49 | ## v0.1.1 50 | 51 | - Make a debounced scroll value update in `scrollWatchService.addConfig()` 52 | - Correctly remove the config on element destruction 53 | 54 | ## v0.1.0 55 | 56 | - First release 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Chih-Hsuan Fan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-scroll-watch [![Join the chat at https://gitter.im/pc035860/angular-scroll-watch](https://badges.gitter.im/pc035860/angular-scroll-watch.svg)](https://gitter.im/pc035860/angular-scroll-watch?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 2 | 3 | Scroll-aware AngularJS with ease. 4 | 5 | How much angular do you want? Lovely slides 6 | 7 | #### Features 8 | 9 | - Pure AngularJS, no dependencies 10 | - **style** and **class** directives with scrolling locals 11 | - Supports multiple **stages** 12 | - Triggers scope digest only when needed 13 | - Of course it utilizes [requestAnimationFrame](http://www.html5rocks.com/en/tutorials/speed/animations/) 14 | 15 | #### Examples 16 | 17 | Scroll them to see the effects! 18 | 19 | - [How much angular do you want? (`sw-class` with `ng-repeat`)](http://pc035860.github.io/angular-scroll-watch/example/sw-class-with-ng-repeat/) 20 | - [Lovely slides (`sw-broadcast`)](http://pc035860.github.io/angular-scroll-watch/example/sw-broadcast/) 21 | - [Two controls on the shield (`sw-stage`)](http://pc035860.github.com/angular-scroll-watch/example/sw-stage/) 22 | 23 | Check out the [example directory](example/) for a full list of examples. 24 | 25 | ## Requirements 26 | 27 | AngularJS 1.2+ 28 | 29 | ## Getting started 30 | 31 | Include the `angular-scroll-watch` module with AngularJS script in your page. 32 | 33 | ```html 34 | 35 | 36 | ``` 37 | 38 | Add `pc035860.scrollWatch` to your app module's dependency. 39 | 40 | ```js 41 | angular.module('myApp', ['pc035860.scrollWatch']); 42 | ``` 43 | 44 | ### Install with Bower 45 | 46 | ```sh 47 | bower install angular-scroll-watch 48 | ``` 49 | 50 | ### Install with npm 51 | 52 | ```sh 53 | npm install angular-scroll-watch 54 | ``` 55 | 56 | ## Usage 57 | 58 | ### scroll-watch 59 | 60 | **Type**: `expression` 61 | 62 | Base directive to specify a scroll-watch configuration. **Required for any watching activity.** 63 | 64 | `scroll-watch` should be used at least one of these directives: `sw-style`, `sw-class`, `sw-broadcast` to take effect, since itself is just a configuration directive. 65 | 66 | ```html 67 |
69 |
70 | ``` 71 | 72 | #### Options 73 | 74 | Name | Type | Description | Required 75 | --- | --- | --- | :---: 76 | from | Number | Watch-range starting point. Can be a positive or a negative (calculated from bottom to top) value. Note that `from`'s visual value (**scrollTop**) must be higher than `to`. | Yes 77 | to | Number | Watch-range starting point. Can be a positive or a negative (calculated from bottom to top) value. Note that `to`'s visual value (**scrollTop**) musts be lower than `from`. | Yes 78 | stage | String | Specify the stage name to watch for scrolling. Stages are defined via `sw-stage`. If no stage is specified, default to browser window. | No 79 | digestSync | Boolean | Normally, `scroll-watch` only reevaluate watchs on `scroll` event fired. Setting `digestSync` to `true` will force `scroll-watch` to do the reevaluation everytime the binded scope gets digested. | No 80 | 81 | #### Examples 82 | 83 | - [All examples listed](example/) 84 | 85 | 86 | ### sw-style 87 | 88 | **Type**: `expression` 89 | 90 | Provides basically the same function with [built-in `ng-style`](https://docs.angularjs.org/api/ng/directive/ngStyle). 91 | 92 | `sw-style` gets reevaluated when the target stage firse `scroll` event or the scope it belongs to get digested (available with `digestSync` option set to `true`). 93 | 94 | There are couple of **locals** available in the expression. See [Locals](#locals) section for more information. 95 | 96 | ```html 97 |
99 |
100 | ``` 101 | 102 | #### Examples 103 | 104 | - [`sw-style` basic](http://pc035860.github.io/angular-scroll-watch/example/sw-style/) 105 | - [`sw-style` touch-enabled](http://pc035860.github.io/angular-scroll-watch/example/sw-style-touch/) 106 | - [Digest sync](http://pc035860.github.com/angular-scroll-watch/example/digest-sync/) 107 | 108 | 109 | ### sw-class 110 | 111 | **Type**: `expression` 112 | 113 | Provides basically the same function with [built-in `ng-class`](https://docs.angularjs.org/api/ng/directive/ngClass). All the animation goodies added after AngularJS 1.2 are also supported. 114 | 115 | `sw-class` gets reevaluated when the target stage fires `scroll` event or the scope it belongs to get digested (available with `digestSync` option set to `true`). 116 | 117 | There are couple of **locals** available in the expression. See [Locals](#locals) section for more information. 118 | 119 | ```html 120 |
127 |
128 | ``` 129 | 130 | #### Examples 131 | 132 | - [`sw-class` basic](http://pc035860.github.com/angular-scroll-watch/example/sw-class/) 133 | - [`sw-class` with `ng-repeat`](http://pc035860.github.com/angular-scroll-watch/example/sw-class-with-ng-repeat/) 134 | 135 | 136 | ### sw-broadcast 137 | 138 | **Type**: `expression` 139 | 140 | `$broadcast`(or `$emit`) certain event when specified condition expression result changes from `false` to `true` or from `true` to `false`. 141 | 142 | **Note that conditions must be written as String rather than Expression.** 143 | 144 | ```html 145 |
149 |
150 | ``` 151 | 152 | By default, all the events `$broadcast`(or `$emit`) by `sw-broadcast` will be **inside the digest loop**. From time to time, you might need the event to be `$broadcast`(or `$emit`) on every stage `scroll`. Setting the condition to `true` will do the work, and no longer trigger the scope digest due to performance consideration. 153 | 154 | ```html 155 |
159 |
160 | ``` 161 | 162 | #### The event 163 | 164 | We then receive the event with `$scope.$on`. 165 | 166 | ```js 167 | $scope.$on('event name', function ($evt, active, locals) { 168 | /** 169 | * active 170 | * 171 | * The evaluation result of the condition. 172 | * Will be `null` when used with "keep firing" events. 173 | */ 174 | 175 | /** 176 | * locals 177 | * 178 | * See the Locals section for more information. 179 | */ 180 | }); 181 | ``` 182 | 183 | #### Special options 184 | 185 | To cover various use cases, `sw-broadcast` comes with serveral special options. **They are all optional.** 186 | 187 | Name | Type | Description | 188 | --- | --- | --- 189 | $rootScope | Boolean | `$broadcast`(or `$emit`) the event from `$rootScope`. Default to `false`. 190 | $emit | Boolean | Use `$emit` instead of `$broadcast`. Default to `false`. 191 | 192 | ```html 193 |
199 |
200 | 201 | ``` 202 | 203 | #### Examples 204 | 205 | - [`sw-broadcast` basic](http://pc035860.github.com/angular-scroll-watch/example/sw-broadcast/) 206 | - [`sw-broadcast` through `$emit`](http://pc035860.github.com/angular-scroll-watch/example/sw-broadcast-through-emit/) 207 | - [Locals](http://pc035860.github.com/angular-scroll-watch/example/locals/) 208 | - [Infinite scroll](http://pc035860.github.com/angular-scroll-watch/example/infinite-scroll/) 209 | 210 | 211 | ### sw-stage 212 | 213 | **Type**: `string` (interpolation-ready) 214 | 215 | `sw-stage` let you specify the scrolling element (the container) to watch with a customized name. 216 | 217 | A default stage named `pc035860` (Ya!!), which is the browser window, will always be presented. (And will be used if you don't specify the `stage` option in `scroll-watch`.) 218 | 219 | Basically there's no restriction on the DOM structure of `scroll-watch` and `sw-stage`, even the directive creation order doesn't matter. 220 | 221 | ```html 222 |
227 |
228 | 229 |
230 |
231 |
232 | ``` 233 | 234 | #### Examples 235 | 236 | - [`sw-stage` basic](http://pc035860.github.io/angular-scroll-watch/example/sw-stage/) 237 | - [Locals](http://pc035860.github.io/angular-scroll-watch/example/locals/) 238 | 239 | 240 | ### Locals 241 | 242 | **Locals** here means a set of locally available variables inside our `sw-style`, `sw-class`, `sw-broadcast` expressions, which can be very useful when we're implmenting various effects. 243 | 244 | Their values depend on which stage your expression is watching (specified in `scroll-watch`'s `stage`) and where you're `scroll-watch` located in DOM. 245 | 246 | **All locals are presented as Number.** 247 | 248 | Name | Description 249 | --- | --- 250 | $positive | Scrolled value in pixel. 251 | $negative | Scrolled value in pixel, but calculated from the stage bottom. This is a negative number. 252 | $progress | Scrolling progress. Ranged from `0` to `1`. 253 | $percentage | Scrolling progress presented in percentage. Ranged from `0` to `100`. 254 | $direction | Scrolling direction. `1` means from top to bottom. `-1` means from bottom to top. 255 | $height | The watcher(`scroll-watch`) element's DOM height. 256 | $offsetTop | The watcher(`scroll-watch`) element's **overall top value**. 257 | $stageTop | The watcher(`scroll-watch`) element's **relative top value to the stage**. **This value is only presented when the watcher is a children of its stage.** 258 | 259 | #### Examples 260 | 261 | - [Locals](http://pc035860.github.io/angular-scroll-watch/example/locals/) 262 | - [All the others listed](example/) 263 | 264 | 265 | 266 | ## Development 267 | 268 | Thie module uses [gulp.js](http://gulpjs.com/) for development tasks. 269 | 270 | ### Setup 271 | 272 | Install all the required node packages. 273 | 274 | ```sh 275 | # Install node packages 276 | npm install 277 | ``` 278 | 279 | ### Gulp tasks 280 | 281 | ```sh 282 | # Lint the source file 283 | gulp lint 284 | 285 | # Build 286 | gulp build 287 | 288 | # Watch the source file for auto-build 289 | gulp watch 290 | 291 | 292 | # Serve the example at http://localhost:9000 293 | # Gulp will also watch for source/example changes 294 | gulp example 295 | ``` 296 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-scroll-watch", 3 | "version": "0.5.5", 4 | "description": "Basic scroll-aware directives for AngularJS.", 5 | "main": "build/angular-scroll-watch.js", 6 | "ignore": [ 7 | "**/.*", 8 | "node_modules", 9 | "gulpfile.js", 10 | "package.json", 11 | "test", 12 | "tests", 13 | "index.html" 14 | ], 15 | "dependencies": {}, 16 | "devDependencies": {} 17 | } 18 | -------------------------------------------------------------------------------- /build/angular-scroll-watch.js: -------------------------------------------------------------------------------- 1 | /*! angular-scroll-watch 2 | version: 0.6.1 3 | build date: 2016-2-13 4 | author: [object Object] 5 | https://github.com/pc035860/angular-scroll-watch.git */ 6 | /* jshint node:true */ 7 | /* global define */ 8 | (function (root, factory) { 9 | if (typeof exports === "object" || (typeof module === "object" && module.exports)) { 10 | module.exports = factory(require("angular")); 11 | } else if (typeof define === "function" && define.amd) { 12 | define(["angular"], factory); 13 | } else { 14 | root.returnExports = factory(root.angular); 15 | } 16 | }(this, function (angular) { 17 | 18 | var MODULE_NAME = 'pc035860.scrollWatch'; 19 | 20 | var DIR_STYLE = 'swStyle', 21 | DIR_CLASS = 'swClass', 22 | DIR_BROADCAST = 'swBroadcast', 23 | DIR_STAGE = 'swStage'; 24 | 25 | var STAGE_NAME_DEFAULT = 'pc035860'; 26 | 27 | var CACHE_ID_STAGE_POOL = 'scrollWatch.stages'; 28 | 29 | angular.module(MODULE_NAME, []) 30 | 31 | .factory('scrollWatchStageFactory', ["$window", "$document", "$parse", "$log", "$rootScope", "$animate", function ( 32 | $window, $document, $parse, $log, $rootScope, $animate 33 | ) { 34 | // ref: http://davidwalsh.name/function-debounce 35 | var debounce = function (func, wait, immediate) { 36 | var timeout; 37 | return function() { 38 | var context = this, args = arguments; 39 | $window.clearTimeout(timeout); 40 | timeout = $window.setTimeout(function() { 41 | timeout = null; 42 | if (!immediate) func.apply(context, args); 43 | }, wait); 44 | if (immediate && !timeout) func.apply(context, args); 45 | }; 46 | }; 47 | 48 | var requestAnimFrame = (function () { 49 | return $window.requestAnimationFrame || 50 | $window.webkitRequestAnimationFrame || 51 | $window.mozRequestAnimationFrame || 52 | $window.oRequestAnimationFrame || 53 | $window.msRequestAnimationFrame || 54 | function(/* function */ callback, /* DOMElement */ element){ 55 | $window.setTimeout(callback, 1000 / 60); 56 | }; 57 | })(); 58 | 59 | var $win = angular.element($window); 60 | 61 | /** 62 | * Stage class 63 | */ 64 | var Stage = function (name, $elm) { 65 | this.init(name, $elm); 66 | }; 67 | 68 | var p = Stage.prototype; 69 | 70 | p.name = null; 71 | p.element = null; 72 | p.configs = null; 73 | p._configId = 0; 74 | p._binded = false; 75 | 76 | p.init = function (name, $elm) { 77 | this.name = name; 78 | this.element = $elm || null; 79 | 80 | this.scrollHandler = this._digest.bind(this); 81 | this._digestDebounced = debounce(this._digest, 50); 82 | 83 | this._digestDebounced(); 84 | }; 85 | 86 | p.setElement = function ($elm) { 87 | this.element = $elm; 88 | 89 | if (this.configs !== null && !this._binded) { 90 | this._bind(this.element); 91 | } 92 | 93 | this._digestDebounced(); 94 | }; 95 | 96 | p.clearElement = function () { 97 | if (this._binded) { 98 | this._unbind(this.element); 99 | } 100 | this.element = null; 101 | }; 102 | 103 | p.addConfig = function (config) { 104 | angular.forEach(['target', 'from', 'to'], function (key) { 105 | if (angular.isUndefined(config[key])) { 106 | throw new Error('`'+ key +'` should be provided'); 107 | } 108 | }); 109 | 110 | if (this.configs === null) { 111 | this.configs = {}; 112 | 113 | if (this.element !== null) { 114 | this._bind(this.element); 115 | } 116 | } 117 | 118 | this._configId++; 119 | 120 | if (config.styleExpr) { 121 | config.styleGetter = $parse(config.styleExpr); 122 | } 123 | if (config.classExpr) { 124 | config.classGetter = $parse(config.classExpr); 125 | } 126 | if (config.brdcstExpr) { 127 | var buf = config.scope.$eval(config.brdcstExpr); 128 | 129 | if (buf.$rootScope) { 130 | config.brdcstScope = $rootScope; 131 | delete buf.$rootScope; 132 | } 133 | else { 134 | config.brdcstScope = config.scope; 135 | } 136 | 137 | if (buf.$emit) { 138 | config.brdcstIsEmit = true; 139 | delete buf.$emit; 140 | } 141 | else { 142 | config.brdcstIsEmit = false; 143 | } 144 | 145 | config.brdcstList = []; 146 | angular.forEach(buf, function (expr, event) { 147 | var pack = { 148 | event: event 149 | }; 150 | 151 | if (!angular.isString(expr)) { 152 | pack.always = true; 153 | } 154 | else { 155 | pack.condition = $parse(expr); 156 | pack.wasActive = null; 157 | } 158 | 159 | config.brdcstList.push(pack); 160 | }); 161 | } 162 | 163 | this.configs[this._configId] = config; 164 | 165 | this._digestDebounced(); 166 | 167 | return this._configId; 168 | }; 169 | 170 | p.removeConfig = function (configId) { 171 | if (this.configs && this.configs[configId]) { 172 | delete this.configs[configId]; 173 | 174 | if (objectSize(this.configs) === 0) { 175 | this.configs = null; 176 | 177 | if (this.element !== null) { 178 | this._unbind(this.element); 179 | } 180 | } 181 | } 182 | }; 183 | 184 | p.digest = function (configId) { 185 | this._digest(null, configId); 186 | }; 187 | 188 | p.destroy = function () { 189 | this.clearElement(); 190 | this.configs = null; 191 | this.scrollHandler = null; 192 | this._digestDebounced = null; 193 | }; 194 | 195 | p.couldDestroy = function () { 196 | return this.element === null && this.configs === null; 197 | }; 198 | 199 | p._isDefault = function () { 200 | return this.element[0] === $window; 201 | }; 202 | 203 | p._getElementMetrics = function ($elm) { 204 | var rect, metrics, scrollTop, elm, stageTop; 205 | 206 | elm = $elm[0]; 207 | rect = elm.getBoundingClientRect(); 208 | scrollTop = this._scrollTop(); 209 | 210 | metrics = { 211 | offsetTop: rect.top + scrollTop 212 | }; 213 | 214 | if (this._isDefault()) { 215 | stageTop = metrics.offsetTop; 216 | } 217 | else { 218 | stageTop = this._traverseStageTop(elm); 219 | } 220 | 221 | if (angular.isDefined(stageTop) && stageTop !== null) { 222 | metrics.stageTop = stageTop; 223 | } 224 | 225 | return metrics; 226 | }; 227 | 228 | p._traverseStageTop = function (elm) { 229 | var stageElm = this.element[0], 230 | top = 0, cursor, progress; 231 | 232 | var updateProgress = function (cursor, progress) { 233 | if (!progress) { 234 | progress = {}; 235 | } 236 | progress.parentNode = cursor.offsetParent; 237 | progress.offsetTop = cursor.offsetTop; 238 | progress.hitStage = progress.parentNode === stageElm; 239 | return progress; 240 | }; 241 | 242 | var c = 0; 243 | 244 | cursor = elm; 245 | progress = updateProgress(cursor); 246 | 247 | do { 248 | cursor = cursor.parentNode || cursor; 249 | 250 | if (progress.parentNode === cursor) { 251 | top += progress.offsetTop; 252 | 253 | if (progress.hitStage) { 254 | return top; 255 | } 256 | 257 | progress = updateProgress(cursor, progress); 258 | } 259 | else if (cursor === stageElm) { 260 | return top + progress.offsetTop - stageElm.offsetTop; 261 | } 262 | 263 | if (++c >= 10) { 264 | break; 265 | } 266 | } while (cursor.tagName !== 'BODY' || elm === cursor); 267 | 268 | return null; 269 | }; 270 | 271 | p._contentHeight = function () { 272 | if (this._isDefault()) { 273 | var doc = $document[0].documentElement, 274 | body = $document[0].body; 275 | 276 | return Math.max( 277 | body.scrollHeight, doc.scrollHeight, 278 | body.offsetHeight, doc.offsetHeight, 279 | doc.clientHeight 280 | ); 281 | } 282 | return this.element[0].scrollHeight; 283 | }; 284 | 285 | p._containerHeight = function () { 286 | if (this._isDefault()) { 287 | var h1 = $document[0].documentElement.clientHeight, 288 | h2 = $window.innerHeight; 289 | if (h1 > h2) { 290 | return h2; 291 | } 292 | return h1; 293 | } 294 | return this.element[0].offsetHeight; 295 | }; 296 | 297 | p._scrollTop = function () { 298 | if (this._isDefault()) { 299 | return $window.pageYOffset; 300 | } 301 | return this.element[0].scrollTop; 302 | }; 303 | 304 | p._digest = (function () { 305 | var reBpCond = /((?:>|<)?=?)\s*?((?:-(?!p))|(?:p(?!-)))?(\d+)/; 306 | 307 | var _handleStyle, 308 | 309 | _handleClass, _addClasses, _removeClasses, 310 | _digestClassCounts, _updateClasses, 311 | 312 | _handleBrdcst, _apply; 313 | 314 | /** 315 | * Style related functions 316 | */ 317 | _handleStyle = function (locals, config) { 318 | config.target.css(config.styleGetter(config.scope, locals)); 319 | }; 320 | 321 | /** 322 | * Class related functions 323 | */ 324 | _addClasses = function (config, classes) { 325 | var attr = config.attr; 326 | var newClasses = _digestClassCounts(config, classes, 1); 327 | attr.$addClass(newClasses); 328 | }; 329 | _removeClasses = function (config, classes) { 330 | var attr = config.attr; 331 | var newClasses = _digestClassCounts(config, classes, -1); 332 | attr.$removeClass(newClasses); 333 | }; 334 | _digestClassCounts = function (config, classes, count) { 335 | var element = config.target; 336 | var classCounts = element.data('$classCounts') || {}; 337 | var classesToUpdate = []; 338 | angular.forEach(classes, function (className) { 339 | if (count > 0 || classCounts[className]) { 340 | classCounts[className] = (classCounts[className] || 0) + count; 341 | if (classCounts[className] === +(count > 0)) { 342 | classesToUpdate.push(className); 343 | } 344 | } 345 | }); 346 | element.data('$classCounts', classCounts); 347 | return classesToUpdate.join(' '); 348 | }; 349 | _updateClasses = function (config, oldClasses, newClasses) { 350 | var element = config.target; 351 | var toAdd = arrayDifference(newClasses, oldClasses); 352 | var toRemove = arrayDifference(oldClasses, newClasses); 353 | toRemove = _digestClassCounts(config, toRemove, -1); 354 | toAdd = _digestClassCounts(config, toAdd, 1); 355 | 356 | if (toAdd.length === 0) { 357 | $animate.removeClass(element, toRemove); 358 | } else if (toRemove.length === 0) { 359 | $animate.addClass(element, toAdd); 360 | } else { 361 | $animate.setClass(element, toAdd, toRemove); 362 | } 363 | config.scope.$digest(); 364 | }; 365 | _handleClass = function (locals, config) { 366 | var newVal = config.classGetter(config.scope, locals), 367 | oldVal = config._oldClassVal; 368 | var newClasses = arrayClasses(newVal || []); 369 | if (!oldVal) { 370 | _addClasses(config, newClasses); 371 | } 372 | else if (!angular.equals(newVal, oldVal)) { 373 | var oldClasses = arrayClasses(oldVal); 374 | _updateClasses(config, oldClasses, newClasses); 375 | } 376 | config._oldClassVal = shallowCopy(newVal); 377 | }; 378 | 379 | /** 380 | * Broadcast condition -> event 381 | */ 382 | _apply = function (scope, fn) { 383 | var phase = scope.$root.$$phase; 384 | if(phase == '$apply' || phase == '$digest') { 385 | if(fn && (typeof(fn) === 'function')) { 386 | fn(); 387 | } 388 | } else { 389 | scope.$apply(fn); 390 | } 391 | }; 392 | _handleBrdcst = function (locals, config) { 393 | angular.forEach(config.brdcstList, function (v) { 394 | var active, funcName; 395 | 396 | funcName = config.brdcstIsEmit ? '$emit' : '$broadcast'; 397 | 398 | if (v.always) { 399 | config.brdcstScope[funcName](v.event, null, locals); 400 | } 401 | else if (v.condition) { 402 | active = v.condition(config.scope, locals); 403 | if (v.wasActive === null || active !== v.wasActive) { 404 | _apply(config.brdcstScope, function () { 405 | config.brdcstScope[funcName](v.event, active, locals); 406 | }); 407 | } 408 | v.wasActive = active; 409 | } 410 | }); 411 | }; 412 | 413 | function _update(configId) { 414 | var positive, negative, numReverse, processConfig; 415 | 416 | var containerHeight = this._containerHeight(), 417 | contentHeight = this._contentHeight(), 418 | maxScrollTop = contentHeight - containerHeight, 419 | scrollTop = this._scrollTop(); 420 | 421 | var self = this; 422 | 423 | positive = function (negative) { 424 | return maxScrollTop + negative; 425 | }; 426 | negative = function (positive) { 427 | return positive - maxScrollTop; 428 | }; 429 | numReverse = function (num) { 430 | return (num > 0) ? negative(num) : positive(num); 431 | }; 432 | 433 | processConfig = function (config) { 434 | var from, to, locals, progress, elmMetrics; 435 | 436 | from = config.from < 0 ? positive(config.from) : config.from; 437 | to = config.to < 0 ? positive(config.to) : config.to; 438 | 439 | /** 440 | * Create local context 441 | */ 442 | if (scrollTop < from) { 443 | progress = 0; 444 | locals = { 445 | $positive: from, 446 | $negative: negative(from) 447 | }; 448 | } 449 | else if (scrollTop > to) { 450 | progress = 1; 451 | locals = { 452 | $positive: to, 453 | $negative: negative(to) 454 | }; 455 | } 456 | else if (scrollTop >= from && scrollTop <= to) { 457 | progress = (scrollTop - from) / (to - from); 458 | locals = { 459 | $positive: scrollTop, 460 | $negative: negative(scrollTop) 461 | }; 462 | } 463 | 464 | locals.$progress = progress; 465 | locals.$percentage = progress * 100; 466 | 467 | if (!config._lastProgress || config._lastProgress === progress) { 468 | locals.$direction = 0; 469 | } 470 | else if (config._lastProgress > progress) { 471 | locals.$direction = -1; 472 | } 473 | else if (config._lastProgress < progress) { 474 | locals.$direction = 1; 475 | } 476 | 477 | locals.$height = config.target[0].offsetHeight; 478 | angular.forEach( 479 | self._getElementMetrics(config.target), 480 | function (v, k) { 481 | locals['$' + k] = v; 482 | }); 483 | 484 | /** 485 | * Applying 486 | */ 487 | // to style 488 | if (config.styleGetter) { 489 | _handleStyle(locals, config); 490 | } 491 | 492 | // to class 493 | if (config.classGetter) { 494 | _handleClass(locals, config); 495 | } 496 | 497 | // trigger breakpoints 498 | if (config.brdcstList) { 499 | _handleBrdcst(locals, config); 500 | } 501 | 502 | config._lastProgress = progress; 503 | }; 504 | 505 | if (angular.isUndefined(configId)) { 506 | angular.forEach(this.configs, processConfig); 507 | } 508 | else if (this.configs[configId]) { 509 | processConfig(this.configs[configId]); 510 | } 511 | } 512 | 513 | return function ($event, configId) { 514 | if (this.element === null) { 515 | return; 516 | } 517 | 518 | if (this._digesting && angular.isUndefined(configId)) { 519 | return; 520 | } 521 | 522 | var self = this; 523 | 524 | this._digesting = true; 525 | 526 | requestAnimFrame(function () { 527 | _update.call(self, configId); 528 | 529 | self._digesting = false; 530 | }); 531 | }; 532 | }()); 533 | 534 | p._bind = function ($elm) { 535 | this._binded = true; 536 | 537 | if (this._isDefault()) { 538 | $win 539 | .on('scroll', this.scrollHandler) 540 | .on('resize', this.scrollHandler); 541 | } 542 | else { 543 | $win.on('resize', this.scrollHandler); 544 | $elm.on('scroll', this.scrollHandler); 545 | } 546 | }; 547 | 548 | p._unbind = function ($elm) { 549 | this._binded = false; 550 | 551 | if (this._isDefault()) { 552 | $win 553 | .off('scroll', this.scrollHandler) 554 | .off('resize', this.scrollHandler); 555 | } 556 | else { 557 | $win.off('resize', this.scrollHandler); 558 | $elm.off('scroll', this.scrollHandler); 559 | } 560 | }; 561 | 562 | return function scrollWatchStageFactory(name, $elm) { 563 | return new Stage(name, $elm); 564 | }; 565 | }]) 566 | 567 | .service('scrollWatchService', 568 | ["scrollWatchStageFactory", "$window", "$log", "$cacheFactory", function scrollWatchService( 569 | scrollWatchStageFactory, $window, $log, $cacheFactory 570 | ) { 571 | var defaultStage = scrollWatchStageFactory( 572 | STAGE_NAME_DEFAULT, 573 | angular.element($window) 574 | ); 575 | 576 | this.stages = $cacheFactory(CACHE_ID_STAGE_POOL); 577 | 578 | this.stages.put(STAGE_NAME_DEFAULT, defaultStage); 579 | 580 | this.addStage = function (stageName, $elm) { 581 | var stage = this.stages.get(stageName); 582 | 583 | if (stage) { 584 | stage.setElement($elm); 585 | return; 586 | } 587 | 588 | stage = scrollWatchStageFactory(stageName, $elm); 589 | this.stages.put(stageName, stage); 590 | }; 591 | 592 | this.removeStage = function (stageName) { 593 | var stage = this.stages.get(stageName); 594 | 595 | if (stage) { 596 | stage.clearElement(); 597 | this._checkStageDestroy(stage); 598 | } 599 | }; 600 | 601 | this.addConfig = function (config) { 602 | var stageName = config.stage, 603 | stage = this.stages.get(stageName); 604 | 605 | if (!stage) { 606 | // Create a stage without element 607 | stage = scrollWatchStageFactory(stageName); 608 | this.stages.put(stageName, stage); 609 | } 610 | 611 | return [stageName, stage.addConfig(config)]; 612 | }; 613 | 614 | this.removeConfig = function (handle) { 615 | var configId = handle[1], 616 | stage = this._getStage(handle); 617 | 618 | if (stage) { 619 | stage.removeConfig(configId); 620 | this._checkStageDestroy(stage); 621 | } 622 | }; 623 | 624 | this.digest = function (handle) { 625 | var configId = handle[1], 626 | stage = this._getStage(handle); 627 | 628 | if (stage) { 629 | stage.digest(configId); 630 | } 631 | }; 632 | 633 | this._checkStageDestroy = function (stage) { 634 | if (stage.couldDestroy()) { 635 | stage.destroy(); 636 | this.stages.remove(stage.name); 637 | } 638 | }; 639 | 640 | this._getStage = function (handle) { 641 | var stageName = handle[0]; 642 | return this.stages.get(stageName); 643 | }; 644 | }]) 645 | 646 | .directive('scrollWatch', ["scrollWatchService", "$parse", function (scrollWatchService, $parse) { 647 | return { 648 | restrict: 'A', 649 | link: function postLink(scope, iElm, iAttrs) { 650 | var configHandle, deregisterDigestSync; 651 | 652 | if (iAttrs[DIR_STYLE] || iAttrs[DIR_CLASS] || iAttrs[DIR_BROADCAST]) { 653 | scope.$watch(iAttrs.scrollWatch, scrollWatchChange, true); 654 | 655 | iElm.on('$destroy', function () { 656 | if (configHandle) { 657 | scrollWatchService.removeConfig(configHandle); 658 | } 659 | }); 660 | } 661 | 662 | function scrollWatchChange(config) { 663 | if (config && angular.isObject(config)) { 664 | // Make sure it won't modify the scope variable 665 | config = angular.copy(config); 666 | 667 | if (configHandle) { 668 | scrollWatchService.removeConfig(configHandle); 669 | (deregisterDigestSync || angular.noop)(); 670 | } 671 | 672 | config.target = iElm; 673 | config.scope = scope; 674 | config.stage = config.stage || STAGE_NAME_DEFAULT; 675 | 676 | if (iAttrs[DIR_STYLE]) { 677 | config.styleExpr = iAttrs[DIR_STYLE]; 678 | } 679 | 680 | if (iAttrs[DIR_CLASS]) { 681 | config.classExpr = iAttrs[DIR_CLASS]; 682 | config.attr = iAttrs; 683 | } 684 | 685 | if (iAttrs[DIR_BROADCAST]) { 686 | config.brdcstExpr = iAttrs[DIR_BROADCAST]; 687 | } 688 | 689 | if (config.digestSync) { 690 | deregisterDigestSync = scope.$watch(digestSync); 691 | } 692 | 693 | configHandle = scrollWatchService.addConfig(config); 694 | } 695 | } 696 | 697 | function digestSync () { 698 | if (configHandle) { 699 | scrollWatchService.digest(configHandle); 700 | } 701 | } 702 | } 703 | }; 704 | }]) 705 | 706 | .directive('swStage', ["scrollWatchService", function (scrollWatchService) { 707 | return { 708 | restrict: 'A', 709 | link: function postLink(scope, iElm, iAttrs) { 710 | var stageName; 711 | 712 | iAttrs.$observe(DIR_STAGE, function (val) { 713 | if (val) { 714 | if (stageName) { 715 | scrollWatchService.removeStage(stageName); 716 | } 717 | 718 | stageName = val; 719 | scrollWatchService.addStage(stageName, iElm); 720 | } 721 | }); 722 | 723 | iElm.on('$destroy', function () { 724 | if (stageName) { 725 | scrollWatchService.removeStage(stageName); 726 | } 727 | }); 728 | } 729 | }; 730 | }]); 731 | 732 | function objectSize(obj) { 733 | var c = 0; 734 | angular.forEach(obj, function () { 735 | c++; 736 | }); 737 | return c; 738 | } 739 | 740 | function arrayDifference(tokens1, tokens2) { 741 | var values = []; 742 | 743 | outer: 744 | for(var i = 0; i < tokens1.length; i++) { 745 | var token = tokens1[i]; 746 | for(var j = 0; j < tokens2.length; j++) { 747 | if(token == tokens2[j]) continue outer; 748 | } 749 | values.push(token); 750 | } 751 | return values; 752 | } 753 | 754 | function arrayClasses (classVal) { 755 | if (angular.isArray(classVal)) { 756 | return classVal; 757 | } else if (angular.isString(classVal)) { 758 | return classVal.split(' '); 759 | } else if (angular.isObject(classVal)) { 760 | var classes = [], i = 0; 761 | angular.forEach(classVal, function(v, k) { 762 | if (v) { 763 | classes = classes.concat(k.split(' ')); 764 | } 765 | }); 766 | return classes; 767 | } 768 | return classVal; 769 | } 770 | 771 | function shallowCopy(src, dst) { 772 | if (angular.isArray(src)) { 773 | dst = dst || []; 774 | 775 | for ( var i = 0; i < src.length; i++) { 776 | dst[i] = src[i]; 777 | } 778 | } else if (angular.isObject(src)) { 779 | dst = dst || {}; 780 | 781 | for (var key in src) { 782 | if (hasOwnProperty.call(src, key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) { 783 | dst[key] = src[key]; 784 | } 785 | } 786 | } 787 | 788 | return dst || src; 789 | } 790 | 791 | return MODULE_NAME; 792 | })); 793 | -------------------------------------------------------------------------------- /build/angular-scroll-watch.min.js: -------------------------------------------------------------------------------- 1 | /*! angular-scroll-watch 2 | version: 0.6.1 3 | build date: 2016-2-13 4 | author: [object Object] 5 | https://github.com/pc035860/angular-scroll-watch.git */ 6 | !function(t,e){"object"==typeof exports||"object"==typeof module&&module.exports?module.exports=e(require("angular")):"function"==typeof define&&define.amd?define(["angular"],e):t.returnExports=e(t.angular)}(this,function(t){function e(e){var n=0;return t.forEach(e,function(){n++}),n}function n(t,e){var n=[];t:for(var i=0;i=10)break}while("BODY"!==e.tagName||t===e);return null},p._contentHeight=function(){if(this._isDefault()){var t=r[0].documentElement,e=r[0].body;return Math.max(e.scrollHeight,t.scrollHeight,e.offsetHeight,t.offsetHeight,t.clientHeight)}return this.element[0].scrollHeight},p._containerHeight=function(){if(this._isDefault()){var t=r[0].documentElement.clientHeight,e=o.innerHeight;return t>e?e:t}return this.element[0].offsetHeight},p._scrollTop=function(){return this._isDefault()?o.pageYOffset:this.element[0].scrollTop},p._digest=function(){function e(e){var n,i,s,c,a=this._containerHeight(),l=this._contentHeight(),f=l-a,u=this._scrollTop(),h=this;n=function(t){return f+t},i=function(t){return t-f},s=function(t){return t>0?i(t):n(t)},c=function(e){var s,c,a,l;s=e.from<0?n(e.from):e.from,c=e.to<0?n(e.to):e.to,s>u?(l=0,a={$positive:s,$negative:i(s)}):u>c?(l=1,a={$positive:c,$negative:i(c)}):u>=s&&c>=u&&(l=(u-s)/(c-s),a={$positive:u,$negative:i(u)}),a.$progress=l,a.$percentage=100*l,e._lastProgress&&e._lastProgress!==l?e._lastProgress>l?a.$direction=-1:e._lastProgress0||o[t])&&(o[t]=(o[t]||0)+i,o[t]===+(i>0)&&r.push(t))}),s.data("$classCounts",o),r.join(" ")},u=function(t,e,i){var s=t.target,o=n(i,e),r=n(e,i);r=l(t,r,-1),o=l(t,o,1),0===o.length?f.removeClass(s,r):0===r.length?f.addClass(s,o):f.setClass(s,o,r),t.scope.$digest()},r=function(e,n){var o=n.classGetter(n.scope,e),r=n._oldClassVal,a=i(o||[]);if(r){if(!t.equals(o,r)){var l=i(r);u(n,l,a)}}else c(n,a);n._oldClassVal=s(o)},g=function(t,e){var n=t.$root.$$phase;"$apply"==n||"$digest"==n?e&&"function"==typeof e&&e():t.$apply(e)},d=function(e,n){t.forEach(n.brdcstList,function(t){var i,s;s=n.brdcstIsEmit?"$emit":"$broadcast",t.always?n.brdcstScope[s](t.event,null,e):t.condition&&(i=t.condition(n.scope,e),(null===t.wasActive||i!==t.wasActive)&&g(n.brdcstScope,function(){n.brdcstScope[s](t.event,i,e)}),t.wasActive=i)})},function(n,i){if(!(null===this.element||this._digesting&&t.isUndefined(i))){var s=this;this._digesting=!0,h(function(){e.call(s,i),s._digesting=!1})}}}(),p._bind=function(t){this._binded=!0,this._isDefault()?d.on("scroll",this.scrollHandler).on("resize",this.scrollHandler):(d.on("resize",this.scrollHandler),t.on("scroll",this.scrollHandler))},p._unbind=function(t){this._binded=!1,this._isDefault()?d.off("scroll",this.scrollHandler).off("resize",this.scrollHandler):(d.off("resize",this.scrollHandler),t.off("scroll",this.scrollHandler))},function(t,e){return new g(t,e)}}]).service("scrollWatchService",["scrollWatchStageFactory","$window","$log","$cacheFactory",function(e,n,i,s){var o=e(f,t.element(n));this.stages=s(u),this.stages.put(f,o),this.addStage=function(t,n){var i=this.stages.get(t);return i?void i.setElement(n):(i=e(t,n),void this.stages.put(t,i))},this.removeStage=function(t){var e=this.stages.get(t);e&&(e.clearElement(),this._checkStageDestroy(e))},this.addConfig=function(t){var n=t.stage,i=this.stages.get(n);return i||(i=e(n),this.stages.put(n,i)),[n,i.addConfig(t)]},this.removeConfig=function(t){var e=t[1],n=this._getStage(t);n&&(n.removeConfig(e),this._checkStageDestroy(n))},this.digest=function(t){var e=t[1],n=this._getStage(t);n&&n.digest(e)},this._checkStageDestroy=function(t){t.couldDestroy()&&(t.destroy(),this.stages.remove(t.name))},this._getStage=function(t){var e=t[0];return this.stages.get(e)}}]).directive("scrollWatch",["scrollWatchService","$parse",function(e){return{restrict:"A",link:function(n,i,s){function o(o){o&&t.isObject(o)&&(o=t.copy(o),u&&(e.removeConfig(u),(h||t.noop)()),o.target=i,o.scope=n,o.stage=o.stage||f,s[r]&&(o.styleExpr=s[r]),s[c]&&(o.classExpr=s[c],o.attr=s),s[a]&&(o.brdcstExpr=s[a]),o.digestSync&&(h=n.$watch(l)),u=e.addConfig(o))}function l(){u&&e.digest(u)}var u,h;(s[r]||s[c]||s[a])&&(n.$watch(s.scrollWatch,o,!0),i.on("$destroy",function(){u&&e.removeConfig(u)}))}}}]).directive("swStage",["scrollWatchService",function(t){return{restrict:"A",link:function(e,n,i){var s;i.$observe(l,function(e){e&&(s&&t.removeStage(s),s=e,t.addStage(s,n))}),n.on("$destroy",function(){s&&t.removeStage(s)})}}}]),o}); -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # angular-scroll-watch examples 2 | 3 | ### `sw-style` 4 | 5 | - [Basic](http://pc035860.github.com/angular-scroll-watch/example/sw-style/) 6 | 7 | ### `sw-class` 8 | 9 | - [Basic](http://pc035860.github.com/angular-scroll-watch/example/sw-class/) 10 | - [With `ng-repeat`](http://pc035860.github.com/angular-scroll-watch/example/sw-class-with-ng-repeat/) 11 | 12 | ### `sw-broadcast` 13 | 14 | - [Basic](http://pc035860.github.com/angular-scroll-watch/example/sw-broadcast/) 15 | - [Through `$scope.$emit`](http://pc035860.github.com/angular-scroll-watch/example/sw-broadcast-through-emit/) 16 | - [Infinite scroll](http://pc035860.github.com/angular-scroll-watch/example/infinite-scroll/) 17 | 18 | ### `sw-stage` 19 | 20 | - [Basic](http://pc035860.github.com/angular-scroll-watch/example/sw-stage/) 21 | 22 | ### Miscellaneous 23 | 24 | - [Digest sync](http://pc035860.github.com/angular-scroll-watch/example/digest-sync/) 25 | - [Locals](http://pc035860.github.com/angular-scroll-watch/example/locals/) 26 | - [Touch-enabled](http://pc035860.github.com/angular-scroll-watch/example/sw-style-touch/) 27 | -------------------------------------------------------------------------------- /example/assets/digest-count.js: -------------------------------------------------------------------------------- 1 | angular.module('app.digest-count', []) 2 | 3 | .directive('digestCount', function ($rootScope) { 4 | return { 5 | restrict: 'EA', 6 | template: '
digest:
', 7 | link: function (scope, iElm, iAttrs) { 8 | var count = 0, dereg; 9 | 10 | dereg = $rootScope.$watch(function () { 11 | iElm.find('span').text(++count); 12 | }); 13 | 14 | iElm.bind('$destroy', function () { 15 | if (dereg) { 16 | dereg(); 17 | } 18 | }); 19 | } 20 | }; 21 | }); 22 | -------------------------------------------------------------------------------- /example/assets/edit-on-plunker.js: -------------------------------------------------------------------------------- 1 | (function (module) { 2 | 3 | var DEFAULT_CACHE_KEY = 'pc035860'; 4 | 5 | var getCache = function ($cacheFactory) { 6 | var ID = 'to-plunker'; 7 | return $cacheFactory.get(ID) || $cacheFactory(ID); 8 | }; 9 | 10 | module 11 | 12 | .directive('toPlunker', function ($cacheFactory) { 13 | return { 14 | link: function postLink(scope, iElm, iAttrs) { 15 | var scriptPathsCache, cache, cacheKey; 16 | 17 | if (iElm[0].tagName === 'SCRIPT' && angular.isDefined(iAttrs.toPlunker)) { 18 | scriptPathsCache = getCache($cacheFactory); 19 | cacheKey = iAttrs.toPlunker || DEFAULT_CACHE_KEY; 20 | 21 | cache = scriptPathsCache.get(cacheKey); 22 | if (!cache) { 23 | cache = []; 24 | } 25 | cache.push(iAttrs.src); 26 | 27 | scriptPathsCache.put(cacheKey, cache); 28 | } 29 | } 30 | }; 31 | }) 32 | 33 | .directive('editOnPlunker', function (openPlunker, $document, $q, $http, $cacheFactory) { 34 | return { 35 | restrict: 'EA', 36 | template: '', 37 | replace: true, 38 | scope: { 39 | id: '@', 40 | getFiles: '&files', 41 | description: '@', 42 | getTags: '&tags' 43 | }, 44 | link: function postLink(scope, iElm, iAttrs) { 45 | var preparePromise = prepareFiles(); 46 | 47 | scope.edit = function () { 48 | var description = scope.description || $document[0].title, 49 | tags = (scope.getTags || angular.noop)() || []; 50 | 51 | preparePromise.then(function (fileObjs) { 52 | openPlunker(fileObjs, description, tags); 53 | }); 54 | }; 55 | 56 | function prepareFiles () { 57 | var scriptPathsCache = getCache($cacheFactory), 58 | cacheKey = scope.id || DEFAULT_CACHE_KEY, 59 | scripts = scriptPathsCache.get(cacheKey), 60 | files = scope.getFiles(), 61 | promises = []; 62 | 63 | if (scripts && angular.isArray(scripts)) { 64 | files = files.concat(scripts); 65 | } 66 | 67 | angular.forEach(files, function (path) { 68 | var promise = $http.get(path, {transformResponse: transformResponse}) 69 | .then(function (res) { 70 | var content = res.data, 71 | filename = getFilename(path); 72 | 73 | if (filename === 'index.html') { 74 | angular.forEach(scripts, function (scriptPath) { 75 | content = content.replace(scriptPath, getFilename(scriptPath)); 76 | }); 77 | } 78 | 79 | return { 80 | filename: filename, 81 | content: content 82 | }; 83 | }); 84 | promises.push(promise); 85 | }); 86 | 87 | return $q.all(promises); 88 | } 89 | 90 | function transformResponse(data, headerGetter) { 91 | return data; 92 | } 93 | 94 | function getFilename(path) { 95 | return path.substring(path.lastIndexOf('/') + 1); 96 | } 97 | } 98 | }; 99 | }) 100 | 101 | .factory('formPostData', function ($document) { 102 | return function(url, fields) { 103 | var form = angular.element('
'); 104 | angular.forEach(fields, function(field) { 105 | var name = field.name, 106 | value = field.value; 107 | 108 | var input = angular.element(''); 109 | input.attr('value', value); 110 | form.append(input); 111 | }); 112 | $document.find('body').append(form); 113 | form[0].submit(); 114 | form.remove(); 115 | }; 116 | }) 117 | 118 | .factory('openPlunker', function (formPostData) { 119 | return function (files, description, tags) { 120 | var postData = []; 121 | 122 | description = description || ''; 123 | tags = tags || []; 124 | 125 | angular.forEach(files, function (file) { 126 | postData.push({ 127 | name: 'files[' + file.filename + ']', 128 | value: file.content 129 | }); 130 | }); 131 | 132 | angular.forEach(tags, function (tag) { 133 | postData.push({ 134 | name: 'tags[]', 135 | value: tag 136 | }); 137 | }); 138 | 139 | postData.push({ 140 | name: 'private', 141 | value: true 142 | }); 143 | postData.push({ 144 | name: 'description', 145 | value: description 146 | }); 147 | 148 | formPostData('http://plnkr.co/edit/?p=preview', postData); 149 | }; 150 | }); 151 | 152 | 153 | })(angular.module('app.edit-on-plunker', [])); 154 | -------------------------------------------------------------------------------- /example/assets/lesshat-prefixed.less: -------------------------------------------------------------------------------- 1 | // * =========================================================== * 2 | // < LESSHat > 3 | // * =========================================================== * 4 | // 5 | // Made with Energy drinks in Prague, Czech Republic. 6 | // Handcrafted by Petr Brzek, lesshat.com 7 | // Works great with CSS Hat csshat.com 8 | 9 | // version: v3.0.2 (2014-06-26) 10 | 11 | // TABLE OF MIXINS: 12 | // align-content 13 | // align-items 14 | // align-self 15 | // animation 16 | // animation-delay 17 | // animation-direction 18 | // animation-duration 19 | // animation-fill-mode 20 | // animation-iteration-count 21 | // animation-name 22 | // animation-play-state 23 | // animation-timing-function 24 | // appearance 25 | // backface-visibility 26 | // background-clip 27 | // background-image 28 | // background-origin 29 | // background-size 30 | // blur 31 | // border-bottom-left-radius 32 | // border-bottom-right-radius 33 | // border-image 34 | // border-radius 35 | // border-top-left-radius 36 | // border-top-right-radius 37 | // box-shadow 38 | // box-sizing 39 | // brightness 40 | // calc 41 | // column-count 42 | // column-gap 43 | // column-rule 44 | // column-width 45 | // columns 46 | // contrast 47 | // display 48 | // drop-shadow 49 | // filter 50 | // flex 51 | // flex-basis 52 | // flex-direction 53 | // flex-grow 54 | // flex-shrink 55 | // flex-wrap 56 | // font-face 57 | // grayscale 58 | // hue-rotate 59 | // hyphens 60 | // invert 61 | // justify-content 62 | // keyframes 63 | // opacity 64 | // order 65 | // perspective 66 | // perspective-origin 67 | // placeholder 68 | // rotate 69 | // rotate3d 70 | // rotateX 71 | // rotateY 72 | // rotateZ 73 | // saturate 74 | // scale 75 | // scale3d 76 | // scaleX 77 | // scaleY 78 | // scaleZ 79 | // selection 80 | // sepia 81 | // size 82 | // skew 83 | // skewX 84 | // skewY 85 | // transform 86 | // transform-origin 87 | // transform-style 88 | // transition 89 | // transition-delay 90 | // transition-duration 91 | // transition-property 92 | // transition-timing-function 93 | // translate 94 | // translate3d 95 | // translateX 96 | // translateY 97 | // translateZ 98 | // user-select 99 | 100 | .lh-align-content(...) { 101 | @process: ~`(function(r){return r=r||"stretch"})((function(){var r="@{arguments}";return r=r.replace(/^\[|\]$/g,"")})())`; 102 | @process_ms: ~`(function(t){return t=t||"stretch","flex-start"==t?t="start":"flex-end"==t?t="end":"space-between"==t?t="justify":"space-around"==t&&(t="distribute"),t})((function(){var r="@{arguments}";return r=r.replace(/^\[|\]$/g,"")})())`; 103 | -webkit-align-content: @process; 104 | -ms-flex-line-pack: @process_ms; 105 | align-content: @process; 106 | } 107 | 108 | .lh-align-items(...) { 109 | @process_olderwebkit: ~`(function(t){return t=t||"stretch","flex-start"==t?t="start":"flex-end"==t&&(t="end"),t})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 110 | @process_moz: ~`(function(t){return t=t||"stretch","flex-start"==t?t="start":"flex-end"==t&&(t="end"),t})((function(){var t="@{arguments}";return t=t.replace(/^\[|\]$/g,"")})())`; 111 | @process: ~`(function(t){return t=t||"stretch"})((function(){var t="@{arguments}";return t=t.replace(/^\[|\]$/g,"")})())`; 112 | @process_ms: ~`(function(t){return t=t||"stretch","flex-start"==t?t="start":"flex-end"==t&&(t="end"),t})((function(){var t="@{arguments}";return t=t.replace(/^\[|\]$/g,"")})())`; 113 | -webkit-box-align: @process_olderwebkit; 114 | -moz-box-align: @process_moz; 115 | -webkit-align-items: @process; 116 | -ms-flex-align: @process_ms; 117 | align-items: @process; 118 | } 119 | 120 | .lh-align-self(...) { 121 | @process: ~`(function(t){return t=t||"auto"})((function(){var t="@{arguments}";return t=t.replace(/^\[|\]$/g,"")})())`; 122 | @process_ms: ~`(function(t){return t=t||"auto","flex-start"==t?t="start":"flex-end"==t&&(t="end"),t})((function(){var t="@{arguments}";return t=t.replace(/^\[|\]$/g,"")})())`; 123 | -webkit-align-self: @process; 124 | -ms-flex-item-align: @process_ms; 125 | align-self: @process; 126 | } 127 | 128 | .lh-animation(...) { 129 | @process: ~`(function(t){return t=t||"none",/^[^, ]*,/.test(t)&&(t=t.replace(/(?:,)(?![^(]*\))/g,"")),t})((function(){var t="@{arguments}";return t=t.replace(/^\[|\]$/g,"")})())`; 130 | -webkit-animation: @process; 131 | -moz-animation: @process; 132 | -o-animation: @process; 133 | animation: @process; 134 | } 135 | 136 | .lh-animation-delay(...) { 137 | @process: ~`(function(t){t=t||"0";var r=/(?:\d)(?:ms|s)/gi,e=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(t)||"0"===t||(t=t.replace(e,function(t){return t+=parseFloat(t,10)>10?"ms":"s"})),t})((function(){var t="@{arguments}";return t=t.replace(/^\[|\]$/g,"")})())`; 138 | -webkit-animation-delay: @process; 139 | -moz-animation-delay: @process; 140 | -o-animation-delay: @process; 141 | animation-delay: @process; 142 | } 143 | 144 | .lh-animation-direction(...) { 145 | @process: ~`(function(r){return r||"normal"})((function(){var r="@{arguments}";return r=r.replace(/^\[|\]$/g,"")})())`; 146 | -webkit-animation-direction: @process; 147 | -moz-animation-direction: @process; 148 | -o-animation-direction: @process; 149 | animation-direction: @process; 150 | } 151 | 152 | .lh-animation-duration(...) { 153 | @process: ~`(function(r){r=r||"0";var t=/ms|s/gi,e=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return t.test(r)||"0"===r||(r=r.replace(e,function(r){return r+=parseFloat(r,10)>10?"ms":"s"})),r})((function(){var r="@{arguments}";return r=r.replace(/^\[|\]$/g,"")})())`; 154 | -webkit-animation-duration: @process; 155 | -moz-animation-duration: @process; 156 | -o-animation-duration: @process; 157 | animation-duration: @process; 158 | } 159 | 160 | .lh-animation-fill-mode(...) { 161 | @process: ~`(function(r){return r||"none"})((function(){var r="@{arguments}";return r=r.replace(/^\[|\]$/g,"")})())`; 162 | -webkit-animation-fill-mode: @process; 163 | -moz-animation-fill-mode: @process; 164 | -o-animation-fill-mode: @process; 165 | animation-fill-mode: @process; 166 | } 167 | 168 | .lh-animation-iteration-count(...) { 169 | @process: ~`(function(r){return r||"0"})((function(){var r="@{arguments}";return r=r.replace(/^\[|\]$/g,"")})())`; 170 | -webkit-animation-iteration-count: @process; 171 | -moz-animation-iteration-count: @process; 172 | -o-animation-iteration-count: @process; 173 | animation-iteration-count: @process; 174 | } 175 | 176 | .lh-animation-name(...) { 177 | @process: ~`(function(r){return r||"none"})((function(){var r="@{arguments}";return r=r.replace(/^\[|\]$/g,"")})())`; 178 | -webkit-animation-name: @process; 179 | -moz-animation-name: @process; 180 | -o-animation-name: @process; 181 | animation-name: @process; 182 | } 183 | 184 | .lh-animation-play-state(...) { 185 | @process: ~`(function(r){return r||"running"})((function(){var r="@{arguments}";return r=r.replace(/^\[|\]$/g,"")})())`; 186 | -webkit-animation-play-state: @process; 187 | -moz-animation-play-state: @process; 188 | -o-animation-play-state: @process; 189 | animation-play-state: @process; 190 | } 191 | 192 | .lh-animation-timing-function(...) { 193 | @process: ~`(function(r){return r||"ease"})((function(){var r="@{arguments}";return r=r.replace(/^\[|\]$/g,"")})())`; 194 | -webkit-animation-timing-function: @process; 195 | -moz-animation-timing-function: @process; 196 | -o-animation-timing-function: @process; 197 | animation-timing-function: @process; 198 | } 199 | 200 | .lh-appearance(...) { 201 | @process: ~`(function(r){return r||"none"})((function(){var r="@{arguments}";return r=r.replace(/^\[|\]$/g,"")})())`; 202 | -webkit-appearance: @process; 203 | -moz-appearance: @process; 204 | appearance: @process; 205 | } 206 | 207 | .lh-backface-visibility(...) { 208 | @process: ~`(function(r){return r||"visible"})((function(){var r="@{arguments}";return r=r.replace(/^\[|\]$/g,"")})())`; 209 | -webkit-backface-visibility: @process; 210 | -moz-backface-visibility: @process; 211 | -ms-backface-visibility: @process; 212 | -o-backface-visibility: @process; 213 | backface-visibility: @process; 214 | } 215 | 216 | .lh-background-clip(...) { 217 | @process: ~`(function(r){return r||"border-box"})((function(){var r="@{arguments}";return r=r.replace(/^\[|\]$/g,"")})())`; 218 | -webkit-background-clip: @process; 219 | -moz-background-clip: @process; 220 | background-clip: @process; 221 | } 222 | 223 | .lh-background-image(...) { 224 | @process_ms: ~`(function(t){function e(t){var e,r,n,a,s,i,u,o,g="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",c=0,l=0,f="",d=[];if(!t)return t;do e=t.charCodeAt(c++),r=t.charCodeAt(c++),n=t.charCodeAt(c++),o=e<<16|r<<8|n,a=63&o>>18,s=63&o>>12,i=63&o>>6,u=63&o,d[l++]=g.charAt(a)+g.charAt(s)+g.charAt(i)+g.charAt(u);while(c',svg_start:'',linear_gradient_start:'",radial_gradient_end:"",rect_linear:'',rect_radial:'',svg_end:""};if(r.length){r.forEach(function(t){var e={};if(Object.keys(a).some(function(r){return t.indexOf(r)>=0?(e.svg_direction=a[r],!0):(e.svg_direction=!1,void 0)}),/linear/.test(t))e.svg_type="linear";else if(/radial/.test(t))e.svg_type="radial";else if(!/linear/.test(t)&&!/radial/.test(t))return e.url=t.trim(),e.svg_type="url",e.svg_direction=!0,n.push(e),!1;var r=t.match(/rgb|#[a-zA-Z0-9]|hsl/g).length;e.svg_stops=[],t=t.replace(/transparent/g,"rgba(0,0,0,0)"),t.match(/#[a-zA-Z0-9]/g)&&t.match(/(#[a-zA-Z0-9]+)\s*(\d+%)?/g).forEach(function(t){t=t.split(" "),e.svg_stops.push('')}),t.match(/rgba?\(\d+,\s*\d+,\s*\d+(?:,\s*(0|1|\.\d+|0\.\d+))?\)/g)&&t.replace(/rgba?\((\d+,\s*\d+,\s*\d+)(?:,\s*(0|1|\.\d+|0\.\d+))?\)\s*(\d+%)?/g,function(t,r,n,a){e.svg_stops.push('')}),t.match(/hsla?\((\d+,\s*\d+%,\s*\d+%),\s*(0|1|\.\d+|0\.\d+)\)/g)&&t.replace(/hsla?\((\d+,\s*\d+%,\s*\d+%),\s*(0|1|\.\d+|0\.\d+)\)\s*(\d+%)?/g,function(t,r,n,a){e.svg_stops.push('')});var s=Math.floor(100/(r-1));e.svg_stops.forEach(function(t,r){/offset="false"/.test(t)&&(e.svg_stops[r]=t.replace(/offset="false"/,'offset="'+s*r+'%"'))}),e.svg_stops.sort(function(t,e){return t=t.match(/offset="(\d+)%"/),e=e.match(/offset="(\d+)%"/),2==t.length&&2==e.length?t[1]-e[1]:void 0}),n.push(e)});var i=[],u=n.every(function(t){for(var e in t)if(0==t[e]||0==t[e].length)return!1;return!0});if(!u)return 8121991;n.forEach(function(t,e){("linear"==t.svg_type||"radial"==t.svg_type)&&(i[e]=s.xml+s.svg_start),"linear"==t.svg_type?(i[e]+=s.linear_gradient_start+" "+t.svg_direction+">",t.svg_stops.forEach(function(t){i[e]+=t}),i[e]+=s.linear_gradient_end,i[e]+=s.rect_linear,i[e]+=s.svg_end):"radial"==t.svg_type?(i[e]+=s.radial_gradient_start+" "+t.svg_direction+">",t.svg_stops.forEach(function(t){i[e]+=t}),i[e]+=s.radial_gradient_end,i[e]+=s.rect_radial,i[e]+=s.svg_end):"url"==t.svg_type&&(i[e]=t.url)}),i.forEach(function(t,r){/<\?xml version="1.0" \?>/g.test(t)&&(i[r]=s.uri_data+e(t)+")")}),t=i.join(",")}return t})((function(){var r="@{arguments}";return r=r.replace(/^\[|\]$/g,"")})())`; 225 | @process_webkit: ~`(function(t){if(t=t||8121991,8121991==t)return t;var e={"to bottom":"top","to left":"right","to top":"bottom","to right":"left","ellipse at center":"center, ellipse cover","circle closest-side":"center center, circle contain","circle farthest-corner":"center center, circle cover","circle farthest-side":"center center, circle cover","ellipse closest-side":"center center, ellipse contain","ellipse farthest-corner":"center center, ellipse cover","ellipse farthest-side":"center center, ellipse cover"},r=/(radial-gradient\()([a-z- ]+)at\s+(\w+%?)\s*(\w*%?)/g,n=Object.keys(e);return n.some(function(n){return t.indexOf(n)>=0?(t=t.replace(new RegExp(n+"(?![ a-z0-9])","g"),e[n]),!0):(r.test(t)&&(t=t.replace(r,function(t,e,r,n,a){return e.trim()+n.trim()+" "+a.trim()+","+r.replace(/closest-side/g,"contain").replace(/farthest-corner/g,"cover").trim()})),void 0)}),t=t.replace(/(\d+)\s*deg/g,function(t,e){return 90-e+"deg"}).replace(/(linear|radial)-gradient/g,"-webkit-$1-gradient")})((function(){var t="@{arguments}";return t=t.replace(/^\[|\]$/g,"")})())`; 226 | @process_moz: ~`(function(e){if(e=e||8121991,8121991==e)return e;var t={"to bottom":"top","to left":"right","to top":"bottom","to right":"left","ellipse at center":"center, ellipse cover","circle closest-side":"center center, circle contain","circle farthest-corner":"center center, circle cover","circle farthest-side":"center center, circle cover","ellipse closest-side":"center center, ellipse contain","ellipse farthest-corner":"center center, ellipse cover","ellipse farthest-side":"center center, ellipse cover"},r=/(radial-gradient\()([a-z- ]+)at\s+(\w+%?)\s*(\w*%?)/g,n=Object.keys(t);return n.some(function(n){return e.indexOf(n)>=0?(e=e.replace(new RegExp(n+"(?![ a-z0-9])","g"),t[n]),!0):(r.test(e)&&(e=e.replace(r,function(e,t,r,n,a){return t.trim()+n.trim()+" "+a.trim()+","+r.replace(/closest-side/g,"contain").replace(/farthest-corner/g,"cover").trim()})),void 0)}),e=e.replace(/(\d+)\s*deg/g,function(e,t){return 90-t+"deg"}).replace(/(linear|radial)-gradient/g,"-moz-$1-gradient")})((function(){var t="@{arguments}";return t=t.replace(/^\[|\]$/g,"")})())`; 227 | @process_opera: ~`(function(e){if(e=e||8121991,8121991==e)return e;var t={"to bottom":"top","to left":"right","to top":"bottom","to right":"left","ellipse at center":"center, ellipse cover","circle closest-side":"center center, circle contain","circle farthest-corner":"center center, circle cover","circle farthest-side":"center center, circle cover","ellipse closest-side":"center center, ellipse contain","ellipse farthest-corner":"center center, ellipse cover","ellipse farthest-side":"center center, ellipse cover"},r=/(radial-gradient\()([a-z- ]+)at\s+(\w+%?)\s*(\w*%?)/g,n=Object.keys(t);return n.some(function(n){return e.indexOf(n)>=0?(e=e.replace(new RegExp(n+"(?![ a-z0-9])","g"),t[n]),!0):(r.test(e)&&(e=e.replace(r,function(e,t,r,n,a){return t.trim()+n.trim()+" "+a.trim()+","+r.replace(/closest-side/g,"contain").replace(/farthest-corner/g,"cover").trim()})),void 0)}),e=e.replace(/(\d+)\s*deg/g,function(e,t){return 90-t+"deg"}).replace(/(linear|radial)-gradient/g,"-o-$1-gradient")})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 228 | @process: ~`(function(e){if(e=e||8121991,8121991==e)return e;var t={top:"to bottom",right:"to left",bottom:"to top",left:"to right"},r=Object.keys(t);return r.some(function(r){return e.indexOf(r)>=0&&!new RegExp("to\\s+"+r+"|at\\s+"+r,"g").test(e)?(e=e.replace(new RegExp(r),t[r]),!0):void 0}),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 229 | background-image: @process_ms; 230 | background-image: @process_webkit; 231 | background-image: @process_moz; 232 | background-image: @process_opera; 233 | background-image: @process; 234 | } 235 | 236 | .lh-background-origin(...) { 237 | @process: ~`(function(e){return e||"padding-box"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 238 | -webkit-background-origin: @process; 239 | -moz-background-origin: @process; 240 | background-origin: @process; 241 | } 242 | 243 | .lh-background-size(...) { 244 | @process: ~`(function(e){e=e||"auto auto";var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 245 | -webkit-background-size: @process; 246 | -moz-background-size: @process; 247 | background-size: @process; 248 | } 249 | 250 | .lh-blur(...) { 251 | @process: ~`(function(e){e=e||"0";var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 252 | -webkit-filter: blur(@process); 253 | -moz-filter: blur(@process); 254 | -ms-filter: blur(@process); 255 | filter: blur(@process); 256 | } 257 | 258 | .lh-border-bottom-left-radius(...) { 259 | @process: ~`(function(e){e=e||"0";var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 260 | -webkit-border-bottom-left-radius: @process; -webkit-background-clip: padding-box; 261 | -moz-border-radius-bottomleft: @process; -moz-background-clip: padding; 262 | border-bottom-left-radius: @process; background-clip: padding-box; 263 | } 264 | 265 | .lh-border-bottom-right-radius(...) { 266 | @process: ~`(function(e){e=e||"0";var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 267 | -webkit-border-bottom-right-radius: @process; -webkit-background-clip: padding-box; 268 | -moz-border-radius-bottomright: @process; -moz-background-clip: padding; 269 | border-bottom-right-radius: @process; background-clip: padding-box; 270 | } 271 | 272 | .lh-border-image(...) { 273 | @process: ~`(function(e){return e=e||8121991,/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 274 | -webkit-border-image: @process; 275 | -moz-border-image: @process; 276 | -o-border-image: @process; 277 | border-image: @process; 278 | } 279 | 280 | .lh-border-radius(...) { 281 | @process: ~`(function(e){e=e||"0";var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 282 | -webkit-border-radius: @process; -webkit-background-clip: padding-box; 283 | -moz-border-radius: @process; -moz-background-clip: padding; 284 | border-radius: @process; background-clip: padding-box; 285 | } 286 | 287 | .lh-border-top-left-radius(...) { 288 | @process: ~`(function(e){e=e||"0";var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 289 | -webkit-border-top-left-radius: @process; -webkit-background-clip: padding-box; 290 | -moz-border-radius-topleft: @process; -moz-background-clip: padding; 291 | border-top-left-radius: @process; background-clip: padding-box; 292 | } 293 | 294 | .lh-border-top-right-radius(...) { 295 | @process: ~`(function(e){e=e||"0";var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 296 | -webkit-border-top-right-radius: @process; -webkit-background-clip: padding-box; 297 | -moz-border-radius-topright: @process; -moz-background-clip: padding; 298 | border-top-right-radius: @process; background-clip: padding-box; 299 | } 300 | 301 | .lh-box-shadow(...) { 302 | @process: ~`(function(e){e=e||"0";var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 303 | -webkit-box-shadow: @process; 304 | -moz-box-shadow: @process; 305 | box-shadow: @process; 306 | } 307 | 308 | .lh-box-sizing(...) { 309 | @process: ~`(function(e){return e=e||"content-box"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 310 | -webkit-box-sizing: @process; 311 | -moz-box-sizing: @process; 312 | box-sizing: @process; 313 | } 314 | 315 | .lh-brightness(...) { 316 | @process: ~`(function(e){return e=e||"1"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 317 | -webkit-filter: brightness(@process); 318 | -moz-filter: brightness(@process); 319 | -ms-filter: brightness(@process); 320 | filter: brightness(@process); 321 | } 322 | 323 | .lh-calc(...) { 324 | @process: ~`(function(e){function t(t,r){var a=");\n",c=n.split(","),i=c[0]+":"+t+"("+(c[1].trim()||0)+a;"start"==r?e="0;\n"+i:e+=i}e=e||8121991;var r="@{state}",n=e;if(8121991==e)return e;switch(r){case"1":t("-webkit-calc","start"),t("-moz-calc"),t("calc");break;case"2":t("-webkit-calc","start"),t("-moz-calc");break;case"3":t("-webkit-calc","start"),t("calc");break;case"4":t("-webkit-calc","start");break;case"5":t("-moz-calc","start"),t("calc");break;case"6":t("-moz-calc","start");break;case"7":t("calc","start")}return e=e.replace(/;$/g,"")})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 325 | @state: 1; -lh-property: @process; 326 | 327 | } 328 | 329 | .lh-column-count(...) { 330 | @process: ~`(function(e){return e=e||"auto"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 331 | -webkit-column-count: @process; 332 | -moz-column-count: @process; 333 | column-count: @process; 334 | } 335 | 336 | .lh-column-gap(...) { 337 | @process: ~`(function(e){e=e||"normal";var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 338 | -webkit-column-gap: @process; 339 | -moz-column-gap: @process; 340 | column-gap: @process; 341 | } 342 | 343 | .lh-column-rule(...) { 344 | @process: ~`(function(e){e=e||"medium none black";var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 345 | -webkit-column-rule: @process; 346 | -moz-column-rule: @process; 347 | column-rule: @process; 348 | } 349 | 350 | .lh-column-width(...) { 351 | @process: ~`(function(e){e=e||"auto";var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 352 | -webkit-column-width: @process; 353 | -moz-column-width: @process; 354 | column-width: @process; 355 | } 356 | 357 | .lh-columns(...) { 358 | @process: ~`(function(e){e=e||"auto auto";var t=/^\d+$/;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,""),e=e.split(" ")),t.test(e[0])&&(e[0]=e[0]+"px"),e.join(" ")})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 359 | -webkit-columns: @process; 360 | -moz-columns: @process; 361 | columns: @process; 362 | } 363 | 364 | .lh-contrast(...) { 365 | @process: ~`(function(e){e=e||"100%";var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"%"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 366 | -webkit-filter: ~"contrast(@{process})"; 367 | -moz-filter: ~"contrast(@{process})"; 368 | -ms-filter: ~"contrast(@{process})"; 369 | filter: ~"contrast(@{process})"; 370 | } 371 | 372 | .lh-display(...) { 373 | @process_oldwebkit: ~`(function(e){return e="flex"==e||"inline-flex"==e?"-webkit-box":8121991})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 374 | @process_moz: ~`(function(e){return e="flex"==e||"inline-flex"==e?"-moz-box":8121991})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 375 | @process_webkit: ~`(function(e){return e="flex"==e||"inline-flex"==e?"-webkit-"+e:8121991})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 376 | @process_ms: ~`(function(e){return e="flex"==e?"-ms-flexbox":"inline-flex"==e?"-ms-inline-flexbox":8121991})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 377 | @process: ~`(function(e){return"flex"!=e&&"inline-flex"!=e&&(e=8121991),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 378 | display: @process_oldwebkit; 379 | display: @process_moz; 380 | display: @process_webkit; 381 | display: @process_ms; 382 | display: @process; 383 | } 384 | 385 | .lh-drop-shadow(...) { 386 | @process: ~`(function(e){if(e=e||8121991,8121991==e)return e;var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 387 | -webkit-filter: drop-shadow(@process); 388 | -moz-filter: drop-shadow(@process); 389 | -ms-filter: drop-shadow(@process); 390 | filter: drop-shadow(@process); 391 | } 392 | 393 | .lh-filter(...) { 394 | @process: ~`(function(e){return e=e||"none",/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 395 | -webkit-filter: @process; 396 | -moz-filter: @process; 397 | -ms-filter: @process; 398 | filter: @process; 399 | } 400 | 401 | .lh-flex(...) { 402 | @process_olderwebkit: ~`(function(e){return/^\d+/.test(e)?e=e.match(/^\d+/)[0]:""==e&&(e="0"),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 403 | @process_moz: ~`(function(e){return/^\d+/.test(e)?e=e.match(/^\d+/)[0]:""==e&&(e="0"),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 404 | @process: ~`(function(e){return e=e||"0 1 auto",/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 405 | -webkit-box-flex: @process_olderwebkit; 406 | -moz-box-flex: @process_moz; 407 | -webkit-flex: @process; 408 | -ms-flex: @process; 409 | flex: @process; 410 | } 411 | 412 | .lh-flex-basis(...) { 413 | @process: ~`(function(e){e=e||"auto";var t=/\d/gi,r=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return t.test(e)&&(e=e.replace(r,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 414 | -webkit-flex-basis: @process; 415 | flex-basis: @process; 416 | } 417 | 418 | .lh-flex-direction(...) { 419 | @process_oldestwebkit: ~`(function(e){return e="row"==e||"column"==e?"normal":"row-reverse"==e||"column-reverse"==e?"reverse":8121991})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 420 | @process_oldermoz: ~`(function(e){return e="row"==e||"column"==e?"normal":"row-reverse"==e||"column-reverse"==e?"reverse":8121991})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 421 | @process_olderwebkit: ~`(function(e){return e="row"==e||"row-reverse"==e?"horizontal":"column"==e||"column-reverse"==e?"vertical":8121991})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 422 | @process_moz: ~`(function(e){return e="row"==e||"row-reverse"==e?"horizontal":"column"==e||"column-reverse"==e?"vertical":8121991})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 423 | @process: ~`(function(e){return e=e||"row"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 424 | -webkit-box-direction: @process_oldestwebkit; 425 | -moz-box-direction: @process_oldermoz; 426 | -webkit-box-orient: @process_olderwebkit; 427 | -moz-box-orient: @process_moz; 428 | -webkit-flex-direction: @process; 429 | -ms-flex-direction: @process; 430 | flex-direction: @process; 431 | } 432 | 433 | .lh-flex-grow(...) { 434 | @process: ~`(function(e){return e=e||"0"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 435 | -webkit-flex-grow: @process; 436 | flex-grow: @process; 437 | } 438 | 439 | .lh-flex-shrink(...) { 440 | @process: ~`(function(e){return e=e||"1"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 441 | -webkit-flex-shrink: @process; 442 | flex-shrink: @process; 443 | } 444 | 445 | .lh-flex-wrap(...) { 446 | @process: ~`(function(e){return e=e||"nowrap"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 447 | -webkit-flex-wrap: @process; 448 | -ms-flex-wrap: @process; 449 | flex-wrap: @process; 450 | } 451 | 452 | .lh-font-face(@fontname, @fontfile, @fontweight:normal, @fontstyle:normal) { 453 | font-family: "@{fontname}"; 454 | src: url("@{fontfile}.eot"); 455 | src: url("@{fontfile}.eot?#iefix") format("embedded-opentype"), 456 | url("@{fontfile}.woff") format("woff"), 457 | url("@{fontfile}.ttf") format("truetype"), 458 | url("@{fontfile}.svg#@{fontname}") format("svg"); 459 | font-weight: @fontweight; 460 | font-style: @fontstyle; 461 | } 462 | 463 | .lh-grayscale(...) { 464 | @process: ~`(function(e){e=e||"0";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"%"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 465 | -webkit-filter: grayscale(@process); 466 | -moz-filter: grayscale(@process); 467 | -ms-filter: grayscale(@process); 468 | filter: grayscale(@process); 469 | } 470 | 471 | .lh-hue-rotate(...) { 472 | @process: ~`(function(e){e=e||"0";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"deg"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 473 | -webkit-filter: hue-rotate(@process); 474 | -moz-filter: hue-rotate(@process); 475 | -ms-filter: hue-rotate(@process); 476 | filter: hue-rotate(@process); 477 | } 478 | 479 | .lh-hyphens(...) { 480 | @process: ~`(function(e){return e=e||"manual"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 481 | -webkit-hyphens: @process; 482 | -moz-hyphens: @process; 483 | -ms-hyphens: @process; 484 | hyphens: @process; 485 | } 486 | 487 | .lh-invert(...) { 488 | @process: ~`(function(e){e=e||"100%";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"%"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 489 | -webkit-filter: invert(@process); 490 | -moz-filter: invert(@process); 491 | -ms-filter: invert(@process); 492 | filter: invert(@process); 493 | } 494 | 495 | .lh-justify-content(...) { 496 | @process_oldestWebkit: ~`(function(e){return e=e||"start","flex-start"==e?e="start":"flex-end"==e?e="end":("space-between"==e||"space-around"==e)&&(e="justify"),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 497 | @process_moz: ~`(function(e){return e=e||"start","flex-start"==e?e="start":"flex-end"==e?e="end":("space-between"==e||"space-around"==e)&&(e="justify"),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 498 | @process_ms: ~`(function(e){return e=e||"start","flex-start"==e?e="start":"flex-end"==e?e="end":"space-between"==e?e="justify":"space-around"==e&&(e="distribute"),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 499 | @process: ~`(function(e){return e=e||"flex-start"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 500 | -webkit-box-pack: @process_oldestWebkit; 501 | -moz-box-pack: @process_moz; 502 | -ms-flex-pack: @process_ms; 503 | -webkit-justify-content: @process; 504 | justify-content: @process; 505 | } 506 | 507 | .lh-keyframes(...) { 508 | @process: ~`(function(e){function r(r,t,c){var i="}\n",u=n.split(/(^[a-zA-Z0-9-]+),/g),s=t+" "+u[1]+"{",o=["-webkit-","-moz-","-ms-",""];c?a.forEach(function(r){-1!==e.indexOf(r)&&(u[2]=u[2].replace(new RegExp(r,"g"),function(e){return c+e}))}):u[2]=u[2].replace(/{([^}]+)}/g,function(e,r){var t=r.split(";");t.forEach(function(e,r){a.forEach(function(n){-1!==e.indexOf(n)&&(t[r]="",o.forEach(function(a){t[r]+=e.trim().replace(new RegExp(n,"g"),function(e){return a+e})+";"}))})});var n=t.join(";").replace(/;;/g,";");return e.replace(r,n)}),s+=u[2]+i,"start"==r?e="0; } \n"+s:"startend"==r?e="0; } \n"+s.replace(i,""):e+="end"==r?s.replace(i,""):s}e=e||8121991;var t="@{state}",n=e;if(8121991==e)return e;var a=["animation","transform","filter"];switch(t){case"1":r("start","@-webkit-keyframes","-webkit-"),r(null,"@-moz-keyframes","-moz-"),r(null,"@-o-keyframes","-o-"),r("end","@keyframes");break;case"2":r("start","@-webkit-keyframes","-webkit-"),r(null,"@-moz-keyframes","-moz-"),r("end","@keyframes");break;case"3":r("start","@-webkit-keyframes","-webkit-"),r(null,"@-moz-keyframes","-moz-"),r("end","@-o-keyframes","-o-");break;case"4":r("start","@-webkit-keyframes","-webkit-"),r(null,"@-o-keyframes","-o-"),r("end","@keyframes");break;case"5":r("start","@-webkit-keyframes","-webkit-"),r("end","@-moz-keyframes","-moz-");break;case"6":r("start","@-webkit-keyframes","-webkit-"),r("end","@-o-keyframes","-o-");break;case"7":r("start","@-webkit-keyframes","-webkit-"),r("end","@keyframes");break;case"8":r("startend","@-webkit-keyframes","-webkit-");break;case"9":r("start","@-moz-keyframes","-moz-"),r(null,"@-o-keyframes","-o-"),r("end","@keyframes");break;case"10":r("start","@-moz-keyframes","-moz-"),r("end","@-o-keyframes","-o-");break;case"11":r("start","@-moz-keyframes","-moz-"),r("end","@keyframes");break;case"12":r("startend","@-moz-keyframes","-moz-");break;case"13":r("start","@-o-keyframes","-o-"),r("end","@keyframes");break;case"14":r("startend","@-o-keyframes","-o-");break;case"15":r("startend","@keyframes")}return e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 509 | @state: 1; lesshat-selector { -lh-property: @process; } 510 | 511 | 512 | 513 | } 514 | 515 | .lh-opacity(...) { 516 | @process_ms: ~`(function(e){return e=e||"filter: alpha(opacity=100)","alpha(opacity="+Math.floor(100*e)+")"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 517 | @process: ~`(function(e){return e=e||"1"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 518 | zoom: 1; filter: @process_ms; 519 | -webkit-opacity: @process; 520 | -moz-opacity: @process; 521 | opacity: @process; 522 | } 523 | 524 | .lh-order(...) { 525 | @process: ~`(function(e){return e=e||"0"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 526 | -webkit-box-ordinal-group: @process; 527 | -moz-box-ordinal-group: @process; 528 | -ms-flex-order: @process; 529 | -webkit-order: @process; 530 | order: @process; 531 | } 532 | 533 | .lh-perspective(...) { 534 | @process: ~`(function(e){e=e||"none";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"px"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 535 | -webkit-perspective: @process; 536 | -moz-perspective: @process; 537 | perspective: @process; 538 | } 539 | 540 | .lh-perspective-origin(...) { 541 | @process: ~`(function(e){e=e||"50% 50%";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"%"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 542 | -webkit-perspective-origin: @process; 543 | -moz-perspective-origin: @process; 544 | perspective-origin: @process; 545 | } 546 | 547 | .lh-placeholder(@color:#aaa, @element: 08121991) { 548 | .inception (@arguments) when not (@element = 08121991) { 549 | @{element}::-webkit-input-placeholder { 550 | color: @color; 551 | } 552 | @{element}:-moz-placeholder { 553 | color: @color; 554 | } 555 | @{element}::-moz-placeholder { 556 | color: @color; 557 | } 558 | @{element}:-ms-input-placeholder { 559 | color: @color; 560 | } 561 | } 562 | .inception (@arguments) when (@element = 08121991) { 563 | &::-webkit-input-placeholder { 564 | color: @color; 565 | } 566 | &:-moz-placeholder { 567 | color: @color; 568 | } 569 | &::-moz-placeholder { 570 | color: @color; 571 | } 572 | &:-ms-input-placeholder { 573 | color: @color; 574 | } 575 | } 576 | .inception(@arguments); 577 | } 578 | 579 | .lh-rotate(...) { 580 | @process: ~`(function(e){e=e||"0";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"deg"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 581 | -webkit-transform: rotate(@process); 582 | -moz-transform: rotate(@process); 583 | -ms-transform: rotate(@process); 584 | -o-transform: rotate(@process); 585 | transform: rotate(@process); 586 | } 587 | 588 | .lh-rotate3d(...) { 589 | @process: ~`(function(e){return e=e||"0, 0, 0, 0",e=e.replace(/,\s*\d+$/,function(e){return e+"deg"})})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 590 | -webkit-transform: rotate3d(@process); 591 | -moz-transform: rotate3d(@process); 592 | -ms-transform: rotate3d(@process); 593 | -o-transform: rotate3d(@process); 594 | transform: rotate3d(@process); 595 | } 596 | 597 | .lh-rotateX(...) { 598 | @process: ~`(function(e){e=e||"0";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"deg"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 599 | -webkit-transform: rotateX(@process); 600 | -moz-transform: rotateX(@process); 601 | -ms-transform: rotateX(@process); 602 | -o-transform: rotateX(@process); 603 | transform: rotateX(@process); 604 | } 605 | 606 | .lh-rotateY(...) { 607 | @process: ~`(function(e){e=e||"0";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"deg"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 608 | -webkit-transform: rotateY(@process); 609 | -moz-transform: rotateY(@process); 610 | -ms-transform: rotateY(@process); 611 | -o-transform: rotateY(@process); 612 | transform: rotateY(@process); 613 | } 614 | 615 | .lh-rotateZ(...) { 616 | @process: ~`(function(e){e=e||"0";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"deg"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 617 | -webkit-transform: rotateZ(@process); 618 | -moz-transform: rotateZ(@process); 619 | -ms-transform: rotateZ(@process); 620 | -o-transform: rotateZ(@process); 621 | transform: rotateZ(@process); 622 | } 623 | 624 | .lh-saturate(...) { 625 | @process: ~`(function(e){e=e||"100%";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"%"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 626 | -webkit-filter: ~"saturate(@{process})"; 627 | -moz-filter: ~"saturate(@{process})"; 628 | -ms-filter: ~"saturate(@{process})"; 629 | filter: ~"saturate(@{process})"; 630 | } 631 | 632 | .lh-scale(...) { 633 | @process: ~`(function(e){return e=e||"1"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 634 | -webkit-transform: scale(@process); 635 | -moz-transform: scale(@process); 636 | -ms-transform: scale(@process); 637 | -o-transform: scale(@process); 638 | transform: scale(@process); 639 | } 640 | 641 | .lh-scale3d(...) { 642 | @process: ~`(function(e){return e=e||"1, 1, 1"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 643 | -webkit-transform: scale3d(@process); 644 | -moz-transform: scale3d(@process); 645 | -ms-transform: scale3d(@process); 646 | -o-transform: scale3d(@process); 647 | transform: scale3d(@process); 648 | } 649 | 650 | .lh-scaleX(...) { 651 | @process: ~`(function(e){return e=e||"1"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 652 | -webkit-transform: scaleX(@process); 653 | -moz-transform: scaleX(@process); 654 | -ms-transform: scaleX(@process); 655 | -o-transform: scaleX(@process); 656 | transform: scaleX(@process); 657 | } 658 | 659 | .lh-scaleY(...) { 660 | @process: ~`(function(e){return e=e||"1"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 661 | -webkit-transform: scaleY(@process); 662 | -moz-transform: scaleY(@process); 663 | -ms-transform: scaleY(@process); 664 | -o-transform: scaleY(@process); 665 | transform: scaleY(@process); 666 | } 667 | 668 | .lh-scaleZ(...) { 669 | @process: ~`(function(e){return e=e||"1"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 670 | -webkit-transform: scaleZ(@process); 671 | -moz-transform: scaleZ(@process); 672 | -ms-transform: scaleZ(@process); 673 | -o-transform: scaleZ(@process); 674 | transform: scaleZ(@process); 675 | } 676 | 677 | .lh-selection(...) { 678 | @process: ~`(function(e){function r(r,t){var a="}\n",c=n.split(","),u=(c[1]||"")+t+"{"+c[0]+a;"start"==r?e="0; } \n"+u:"startend"==r?e="0; } \n"+u.replace(a,""):e+="end"==r?u.replace(a,""):u}e=e||8121991;var t="@{state}",n=e;if(8121991==e)return e;switch(t){case"1":r("start","::selection"),r("end","::-moz-selection");break;case"2":r("startend","::selection");break;case"3":r("startend","::-moz-selection")}return e=e.replace(/;$/g,"")})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 679 | @state: 1; lesshat-selector { -lh-property: @process; } 680 | 681 | } 682 | 683 | .lh-sepia(...) { 684 | @process: ~`(function(e){e=e||"100%";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"%"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 685 | -webkit-filter: sepia(@process); 686 | -moz-filter: sepia(@process); 687 | -ms-filter: sepia(@process); 688 | filter: sepia(@process); 689 | } 690 | 691 | .lh-size(@square) { 692 | @unit: 'px'; 693 | .process(@square) when (ispixel(@square)), (isem(@square)), (ispercentage(@square)), (iskeyword(@square)) { 694 | width: @square; 695 | height: @square; 696 | } 697 | 698 | .process(@square) when not (ispixel(@square)) and not (isem(@square)) and not (ispercentage(@square)) and not (isstring(@square)) and not (iskeyword(@square)) { 699 | width: ~`@{square} + @{unit}`; 700 | height: ~`@{square} + @{unit}`; 701 | } 702 | 703 | .process(@square); 704 | 705 | } 706 | 707 | .lh-size(@width, @height) { 708 | @unit: 'px'; 709 | .process(@width, @height) when (ispixel(@width)), (isem(@width)), (ispercentage(@width)), (iskeyword(@width)) { 710 | .kittens(@height) when (ispixel(@height)), (isem(@height)), (ispercentage(@height)), (iskeyword(@height)) { 711 | width: @width; 712 | height: @height; 713 | } 714 | .kittens(@height) when not (ispixel(@height)) and not (isem(@height)) and not (ispercentage(@height)) and not (iskeyword(@height)) { 715 | width: @width; 716 | height: ~`@{height} + @{unit}`; 717 | } 718 | .kittens(@height); 719 | } 720 | 721 | .process(@width, @height) when (ispixel(@height)), (isem(@height)), (ispercentage(@height)), (iskeyword(@height)) { 722 | .kittens(@width) when (ispixel(@width)), (isem(@width)), (ispercentage(@width)), (iskeyword(@width)) {} 723 | .kittens(@width) when not (ispixel(@width)) and not (isem(@width)) and not (ispercentage(@width)) and not (iskeyword(@width)) { 724 | width: ~`@{width} + @{unit}`; 725 | height: @height; 726 | } 727 | .kittens(@width); 728 | } 729 | 730 | .process(@width, @height) when not (ispixel(@width)) and not (isem(@width)) and not (ispercentage(@width)) and not (iskeyword(@width)) and not (ispixel(@height)) and not (isem(@height)) and not (ispercentage(@height)) and not (iskeyword(@height)) { 731 | width: ~`@{width} + @{unit}`; 732 | height: ~`@{height} + @{unit}`; 733 | } 734 | 735 | .process(@width, @height); 736 | 737 | } 738 | 739 | .lh-skew(...) { 740 | @process: ~`(function(e){e=e||"0";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"deg"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 741 | -webkit-transform: skew(@process); 742 | -moz-transform: skew(@process); 743 | -ms-transform: skew(@process); 744 | -o-transform: skew(@process); 745 | transform: skew(@process); 746 | } 747 | 748 | .lh-skewX(...) { 749 | @process: ~`(function(e){e=e||"0";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"deg"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 750 | -webkit-transform: skewX(@process); 751 | -moz-transform: skewX(@process); 752 | -ms-transform: skewX(@process); 753 | -o-transform: skewX(@process); 754 | transform: skewX(@process); 755 | } 756 | 757 | .lh-skewY(...) { 758 | @process: ~`(function(e){e=e||"0";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"deg"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 759 | -webkit-transform: skewY(@process); 760 | -moz-transform: skewY(@process); 761 | -ms-transform: skewY(@process); 762 | -o-transform: skewY(@process); 763 | transform: skewY(@process); 764 | } 765 | 766 | .lh-transform(...) { 767 | @process: ~`(function(e){e=e||"none";var r={translate:"px",rotate:"deg",rotate3d:"deg",skew:"deg"};/^\w*\(?[a-z0-9.]*\)?/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,""));for(var t in r)e.indexOf(t)>=0&&(e=e.replace(new RegExp(t+"[\\w]?\\([a-z0-9, %]*\\)"),function(e){var n=/(\d+\.?\d*)(?!\w|%)/g;return"rotate3d"==t&&(n=/,\s*\d+$/),e.replace(n,function(e){return e+r[t]})}));return e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 768 | -webkit-transform: @process; 769 | -moz-transform: @process; 770 | -ms-transform: @process; 771 | -o-transform: @process; 772 | transform: @process; 773 | } 774 | 775 | .lh-transform-origin(...) { 776 | @process: ~`(function(e){e=e||"50% 50% 0";var r=/\d/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),r.test(e)&&(e=e.replace(t,function(e){return 0==e&&e||e+"%"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 777 | -webkit-transform-origin: @process; 778 | -moz-transform-origin: @process; 779 | -ms-transform-origin: @process; 780 | -o-transform-origin: @process; 781 | transform-origin: @process; 782 | } 783 | 784 | .lh-transform-style(...) { 785 | @process: ~`(function(e){return e=e||"flat"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 786 | -webkit-transform-style: @process; 787 | -moz-transform-style: @process; 788 | -ms-transform-style: @process; 789 | -o-transform-style: @process; 790 | transform-style: @process; 791 | } 792 | 793 | .lh-transition(...) { 794 | @process_webkit: ~`(function(e){e=e||"all 0 ease 0";var r=["background-size","border-radius","border-bottom-left-radius","border-bottom-right-radius","border-top-left-radius","border-top-right-radius","box-shadow","column","transform","filter"],t="-webkit-",n=/(?:\d)(?:ms|s)/gi,a=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),r.forEach(function(r){-1!==e.indexOf(r)&&(e=e.replace(new RegExp(r,"g"),function(e){return t+e}))}),n.test(e)||"0"===e||(e=e.replace(a,function(e){return e+=parseFloat(e,10)>10?"ms":"s"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 795 | @process_moz: ~`(function(e){e=e||"all 0 ease 0";var r=["background-size","box-shadow","column","transform","filter"],t="-moz-",n=/(?:\d)(?:ms|s)/gi,a=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),r.forEach(function(r){-1!==e.indexOf(r)&&(e=e.replace(new RegExp(r,"g"),function(e){return t+e}))}),n.test(e)||"0"===e||(e=e.replace(a,function(e){return e+=parseFloat(e,10)>10?"ms":"s"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 796 | @process_opera: ~`(function(e){e=e||"all 0 ease 0";var r=["transform"],t="-o-",n=/(?:\d)(?:ms|s)/gi,a=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),r.forEach(function(r){-1!==e.indexOf(r)&&(e=e.replace(new RegExp(r,"g"),function(e){return t+e}))}),n.test(e)||"0"===e||(e=e.replace(a,function(e){return e+=parseFloat(e,10)>10?"ms":"s"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 797 | @process: ~`(function(e){e=e||"all 0 ease 0";var r=["-webkit-","-moz-","-o-",""],t=["column","transform","filter"],n=/(?:\d)(?:ms|s)/gi,a=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%)/gi;/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,""));var c=e.split(/(?:,)(?![^(]*\))/g);return c.forEach(function(e,n){t.forEach(function(t){-1!==e.indexOf(t)&&(c[n]="",r.forEach(function(a,u){c[n]+=e.trim().replace(new RegExp(t,"g"),function(e){return a+e}),u10?"ms":"s"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 798 | -webkit-transition: @process_webkit; 799 | -moz-transition: @process_moz; 800 | -o-transition: @process_opera; 801 | transition: @process; 802 | } 803 | 804 | .lh-transition-delay(...) { 805 | @process: ~`(function(e){e=e||"0";var r=/(?:\d)(?:ms|s)/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)||"0"===e||(e=e.replace(t,function(e){return e+=parseFloat(e,10)>10?"ms":"s"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 806 | -webkit-transition-delay: @process; 807 | -moz-transition-delay: @process; 808 | -o-transition-delay: @process; 809 | transition-delay: @process; 810 | } 811 | 812 | .lh-transition-duration(...) { 813 | @process: ~`(function(e){e=e||"0";var r=/ms|s/gi,t=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%|\.)/gi;return r.test(e)||"0"===e||(e=e.replace(t,function(e){return e+=parseFloat(e,10)>10?"ms":"s"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 814 | -webkit-transition-duration: @process; 815 | -moz-transition-duration: @process; 816 | -o-transition-duration: @process; 817 | transition-duration: @process; 818 | } 819 | 820 | .lh-transition-property(...) { 821 | @process_webkit: ~`(function(e){e=e||"all";var r=["background-size","border-radius","border-bottom-left-radius","border-bottom-right-radius","border-top-left-radius","border-top-right-radius","box-shadow","column","transform","filter"],t="-webkit-";return r.forEach(function(r){-1!==e.indexOf(r)&&(e=e.replace(new RegExp(r,"g"),function(e){return t+e}))}),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 822 | @process_moz: ~`(function(e){e=e||"all";var r=["background-size","box-shadow","column","transform","filter"],t="-moz-";return r.forEach(function(r){-1!==e.indexOf(r)&&(e=e.replace(new RegExp(r,"g"),function(e){return t+e}))}),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 823 | @process_opera: ~`(function(e){e=e||"all";var r=["transform"],t="-o-";return r.forEach(function(r){-1!==e.indexOf(r)&&(e=e.replace(new RegExp(r,"g"),function(e){return t+e}))}),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; 824 | @process: ~`(function(e){e=e||"all";var r=["-webkit-","-moz-","-o-",""],t=["column","transform","filter"],n=e.split(/(?:,)(?![^(]*\))/g);return n.forEach(function(e,a){t.forEach(function(t){-1!==e.indexOf(t)&&(n[a]="",r.forEach(function(c,u){n[a]+=e.trim().replace(new RegExp(t,"g"),function(e){return c+e}),u 2 | 3 | 4 | 5 | 6 | angular-scroll-watch: digest sync 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 35 | 36 | 37 | 38 | 39 | 40 | Fork me on GitHub 41 | 42 | 43 | 44 |
45 | 46 | 48 | 49 |
52 |
53 | 54 |
55 |
60 | 61 |

Scale factor: {{ scaleFactor }}

62 | 63 |
64 |
65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /example/digest-sync/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | #scroll-stage { 6 | height: 2000px; 7 | } 8 | #hero { 9 | -webkit-transform: translate(-50%, -50%); 10 | -moz-transform: translate(-50%, -50%); 11 | -ms-transform: translate(-50%, -50%); 12 | -o-transform: translate(-50%, -50%); 13 | transform: translate(-50%, -50%); 14 | position: fixed; 15 | top: 50%; 16 | left: 50%; 17 | text-align: center; 18 | } 19 | #hero p { 20 | color: #FAC15F; 21 | } 22 | -------------------------------------------------------------------------------- /example/digest-sync/style.less: -------------------------------------------------------------------------------- 1 | @import "../assets/lesshat-prefixed"; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | #scroll-stage { 9 | height: 2000px; 10 | } 11 | 12 | #hero { 13 | .lh-translate(-50%, -50%); 14 | 15 | position: fixed; 16 | top: 50%; 17 | left: 50%; 18 | 19 | text-align: center; 20 | 21 | p { 22 | color: #FAC15F; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/infinite-scroll/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | angular-scroll-watch: infinite scroll 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 82 | 83 | 84 | 85 | 86 |
87 | 88 | 89 | Fork me on GitHub 90 | 91 | 92 |
98 | 99 |
100 |
101 |

Infinite scroll

102 |

Automatically append more items when the scroll distance from bottom is less than 400.

103 |

104 | 105 | 108 |

109 |
110 |
111 | 112 |
113 | 114 |
115 |
116 |
{{ item.id }}
117 |
118 |
119 |
120 | 121 |
122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /example/infinite-scroll/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | background-color: #EEFFD8; 5 | } 6 | #content { 7 | position: relative; 8 | } 9 | .jumbotron { 10 | background-color: transparent; 11 | } 12 | .jumbotron .btn { 13 | position: static !important; 14 | left: auto !important; 15 | top: auto !important; 16 | margin: 0 !important; 17 | } 18 | .item { 19 | -webkit-border-radius: 5px; 20 | -webkit-background-clip: padding-box; 21 | -moz-border-radius: 5px; 22 | -moz-background-clip: padding; 23 | border-radius: 5px; 24 | background-clip: padding-box; 25 | margin-bottom: 10px; 26 | text-align: center; 27 | min-height: 80px; 28 | line-height: 80px; 29 | color: #A662B2; 30 | font-size: 24px; 31 | border: 5px solid #F5BFFF; 32 | } 33 | .item-anim.ng-enter { 34 | -webkit-animation: zoomInUp 800ms both; 35 | -moz-animation: zoomInUp 800ms both; 36 | -o-animation: zoomInUp 800ms both; 37 | animation: zoomInUp 800ms both; 38 | } 39 | .item-anim.ng-enter-stagger { 40 | -webkit-animation-delay: 25ms; 41 | -moz-animation-delay: 25ms; 42 | -o-animation-delay: 25ms; 43 | animation-delay: 25ms; 44 | } 45 | -------------------------------------------------------------------------------- /example/infinite-scroll/style.less: -------------------------------------------------------------------------------- 1 | @import "../assets/lesshat-prefixed"; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | 7 | background-color: #EEFFD8; 8 | } 9 | 10 | #content { 11 | position: relative; 12 | } 13 | 14 | .jumbotron { 15 | background-color: transparent; 16 | .btn { 17 | position: static !important; 18 | left: auto !important; 19 | top: auto !important; 20 | margin: 0 !important; 21 | } 22 | } 23 | 24 | .item { 25 | @h: 80px; 26 | 27 | .lh-border-radius(5px); 28 | 29 | margin-bottom: 10px; 30 | 31 | text-align: center; 32 | min-height: @h; 33 | line-height: @h; 34 | color: #A662B2; 35 | font-size: 24px; 36 | border: 5px solid #F5BFFF; 37 | } 38 | 39 | .item-anim { 40 | &.ng-enter { 41 | .lh-animation(zoomInUp 800ms both); 42 | 43 | &-stagger { 44 | .lh-animation-delay(25ms); 45 | } 46 | 47 | &-active { 48 | 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /example/locals/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | angular-scroll-watch: locals 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 58 | 59 | 60 | 61 | 62 | 63 | Fork me on GitHub 64 | 65 | 66 | 67 |
68 | 69 | 71 | 72 |
78 | 79 |
80 |
81 |
82 |
83 |

Stage:

84 |

Locals (scroll to observe)

85 |
86 |
87 |
88 |

sub stage changes background color after scrolling pass the watcher element.

89 |
91 |
92 |
watcher
99 |
100 |
101 |
102 |
103 |
104 |
105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /example/locals/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | #scroll-stage { 6 | height: 5000px; 7 | } 8 | #locals { 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | width: 100%; 13 | height: 100%; 14 | } 15 | #locals .container { 16 | -webkit-transform: translate(-50%, -50%); 17 | -moz-transform: translate(-50%, -50%); 18 | -ms-transform: translate(-50%, -50%); 19 | -o-transform: translate(-50%, -50%); 20 | transform: translate(-50%, -50%); 21 | position: absolute; 22 | top: 50%; 23 | left: 50%; 24 | } 25 | @media (max-width: 767px) { 26 | #locals .container { 27 | width: 85%; 28 | } 29 | } 30 | #locals pre { 31 | padding: 0; 32 | } 33 | #locals .hljs { 34 | font-size: 140%; 35 | } 36 | #sub-scroll-stage { 37 | -webkit-transition: background-color 350ms ease-out; 38 | -moz-transition: background-color 350ms ease-out; 39 | -o-transition: background-color 350ms ease-out; 40 | transition: background-color 350ms ease-out; 41 | -webkit-border-radius: 4px; 42 | -webkit-background-clip: padding-box; 43 | -moz-border-radius: 4px; 44 | -moz-background-clip: padding; 45 | border-radius: 4px; 46 | background-clip: padding-box; 47 | width: 100%; 48 | height: 348px; 49 | overflow-y: auto; 50 | border: 1px solid #ccc; 51 | } 52 | @media (max-width: 991px) { 53 | #sub-scroll-stage { 54 | height: 150px; 55 | } 56 | } 57 | #sub-scroll-stage.colored { 58 | background-color: rgba(190, 236, 238, 0.38); 59 | } 60 | #sub-scroll-stage .placeholder { 61 | position: relative; 62 | height: 1000px; 63 | } 64 | #sub-scroll-stage .placeholder .dot { 65 | position: absolute; 66 | top: 300px; 67 | left: 0; 68 | width: 100%; 69 | font-size: 24px; 70 | color: white; 71 | text-align: center; 72 | background-color: #3fa4ff; 73 | } 74 | -------------------------------------------------------------------------------- /example/locals/style.less: -------------------------------------------------------------------------------- 1 | @import "../assets/lesshat-prefixed"; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | #scroll-stage { 9 | height: 5000px; 10 | } 11 | 12 | #locals { 13 | position: fixed; 14 | top: 0; 15 | left: 0; 16 | 17 | width: 100%; 18 | height: 100%; 19 | 20 | .container { 21 | .lh-translate(-50%, -50%); 22 | 23 | position: absolute; 24 | top: 50%; 25 | left: 50%; 26 | 27 | @media (max-width: 767px) { 28 | width: 85%; 29 | } 30 | } 31 | 32 | pre { 33 | padding: 0; 34 | } 35 | 36 | .hljs { 37 | font-size: 140%; 38 | } 39 | } 40 | 41 | #sub-scroll-stage { 42 | .lh-transition(background-color 350ms ease-out); 43 | .lh-border-radius(4px); 44 | 45 | width: 100%; 46 | height: 348px; 47 | 48 | overflow-y: auto; 49 | border: 1px solid #ccc; 50 | 51 | @media (max-width: 991px) { 52 | height: 150px; 53 | } 54 | 55 | &.colored { 56 | background-color: rgba(190, 236, 238, 0.38); 57 | } 58 | 59 | .placeholder { 60 | position: relative; 61 | height: 1000px; 62 | 63 | .dot { 64 | position: absolute; 65 | top: 300px; 66 | left: 0; 67 | 68 | width: 100%; 69 | 70 | font-size: 24px; 71 | color: white; 72 | text-align: center; 73 | background-color: rgba(63, 164, 255, 1); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /example/sw-broadcast-through-emit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | angular-scroll-watch: sw-broadcast through $scope.$emit 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 37 | 38 | 39 | 40 | 41 | 42 | Fork me on GitHub 43 | 44 | 45 | 46 |
47 | 48 | 50 | 51 |
62 | 63 |
64 |

Slide 1

65 |
66 |
67 |

Slide 2

68 |
69 |
70 |

Slide 3

71 |
72 |
73 |

Slide 4

74 |
75 |
76 |

Slide 5

77 |
78 | 79 |
80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /example/sw-broadcast-through-emit/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | #scroll-stage { 6 | height: 5000px; 7 | } 8 | .slide { 9 | width: 100%; 10 | height: 100%; 11 | position: fixed; 12 | z-index: 1; 13 | left: 0; 14 | top: 0; 15 | color: white; 16 | } 17 | .slide h2 { 18 | -webkit-transform: translate(-50%, -50%); 19 | -moz-transform: translate(-50%, -50%); 20 | -ms-transform: translate(-50%, -50%); 21 | -o-transform: translate(-50%, -50%); 22 | transform: translate(-50%, -50%); 23 | position: fixed; 24 | top: 50%; 25 | left: 50%; 26 | font-size: 80px; 27 | } 28 | .slide.ng-hide-add, 29 | .slide.ng-hide-remove { 30 | display: block !important; 31 | } 32 | .slide--1 { 33 | background-color: #3498db; 34 | } 35 | .slide--1.ng-hide-add { 36 | -webkit-animation: bounceOut 1s both; 37 | -moz-animation: bounceOut 1s both; 38 | -o-animation: bounceOut 1s both; 39 | animation: bounceOut 1s both; 40 | } 41 | .slide--1.ng-hide-remove { 42 | -webkit-animation: bounceIn 1s 300ms both; 43 | -moz-animation: bounceIn 1s 300ms both; 44 | -o-animation: bounceIn 1s 300ms both; 45 | animation: bounceIn 1s 300ms both; 46 | z-index: 2; 47 | } 48 | .slide--2 { 49 | background-color: #e74c3c; 50 | } 51 | .slide--2.ng-hide-add { 52 | -webkit-animation: zoomOutRight 1s both; 53 | -moz-animation: zoomOutRight 1s both; 54 | -o-animation: zoomOutRight 1s both; 55 | animation: zoomOutRight 1s both; 56 | } 57 | .slide--2.ng-hide-remove { 58 | -webkit-animation: zoomInLeft 1s 300ms both; 59 | -moz-animation: zoomInLeft 1s 300ms both; 60 | -o-animation: zoomInLeft 1s 300ms both; 61 | animation: zoomInLeft 1s 300ms both; 62 | z-index: 2; 63 | } 64 | .slide--3 { 65 | background-color: #34495e; 66 | } 67 | .slide--3.ng-hide-add { 68 | -webkit-animation: fadeOutLeft 1s both; 69 | -moz-animation: fadeOutLeft 1s both; 70 | -o-animation: fadeOutLeft 1s both; 71 | animation: fadeOutLeft 1s both; 72 | } 73 | .slide--3.ng-hide-remove { 74 | -webkit-animation: fadeInRight 1s 300ms both; 75 | -moz-animation: fadeInRight 1s 300ms both; 76 | -o-animation: fadeInRight 1s 300ms both; 77 | animation: fadeInRight 1s 300ms both; 78 | z-index: 2; 79 | } 80 | .slide--4 { 81 | background-color: #1abc9c; 82 | } 83 | .slide--4.ng-hide-add { 84 | -webkit-animation: bounceOutDown 1s both; 85 | -moz-animation: bounceOutDown 1s both; 86 | -o-animation: bounceOutDown 1s both; 87 | animation: bounceOutDown 1s both; 88 | } 89 | .slide--4.ng-hide-remove { 90 | -webkit-animation: bounceInDown 1s 300ms both; 91 | -moz-animation: bounceInDown 1s 300ms both; 92 | -o-animation: bounceInDown 1s 300ms both; 93 | animation: bounceInDown 1s 300ms both; 94 | z-index: 2; 95 | } 96 | .slide--5 { 97 | background-color: #ecf0f1; 98 | color: #333; 99 | } 100 | .slide--5.ng-hide-add { 101 | -webkit-animation: zoomOutUp 1s both; 102 | -moz-animation: zoomOutUp 1s both; 103 | -o-animation: zoomOutUp 1s both; 104 | animation: zoomOutUp 1s both; 105 | } 106 | .slide--5.ng-hide-remove { 107 | -webkit-animation: zoomInUp 1s 300ms both; 108 | -moz-animation: zoomInUp 1s 300ms both; 109 | -o-animation: zoomInUp 1s 300ms both; 110 | animation: zoomInUp 1s 300ms both; 111 | z-index: 2; 112 | } 113 | -------------------------------------------------------------------------------- /example/sw-broadcast-through-emit/style.less: -------------------------------------------------------------------------------- 1 | @import "../assets/lesshat-prefixed"; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | #scroll-stage { 9 | height: 5000px; 10 | } 11 | 12 | .slide { 13 | .lh-size(100%); 14 | 15 | position: fixed; z-index: 1; 16 | left: 0; 17 | top: 0; 18 | 19 | color: white; 20 | 21 | h2 { 22 | .lh-translate(-50%, -50%); 23 | 24 | position: fixed; 25 | top: 50%; 26 | left: 50%; 27 | 28 | font-size: 80px; 29 | } 30 | 31 | &.ng-hide-add, &.ng-hide-remove { 32 | display: block !important; 33 | } 34 | 35 | .setup-anim (@show; @hide) { 36 | &.ng-hide-add { 37 | .lh-animation(@hide 1s both); 38 | } 39 | &.ng-hide-remove { 40 | .lh-animation(@show 1s 300ms both); 41 | z-index: 2; 42 | } 43 | } 44 | 45 | &--1 { 46 | .setup-anim(bounceIn; bounceOut); 47 | background-color: #3498db; 48 | } 49 | &--2 { 50 | .setup-anim(zoomInLeft; zoomOutRight); 51 | background-color: #e74c3c; 52 | } 53 | &--3 { 54 | .setup-anim(fadeInRight; fadeOutLeft); 55 | background-color: #34495e; 56 | } 57 | &--4 { 58 | .setup-anim(bounceInDown; bounceOutDown); 59 | background-color: #1abc9c; 60 | } 61 | &--5 { 62 | .setup-anim(zoomInUp; zoomOutUp); 63 | background-color: #ecf0f1; 64 | color: #333; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /example/sw-broadcast/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | angular-scroll-watch: sw-broadcast 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 41 | 42 | 43 | 44 | 45 | 46 | Fork me on GitHub 47 | 48 | 49 | 50 |
51 | 52 | 54 | 55 |
64 | 65 |
66 |

Slide 1

67 |
68 |
69 |

Slide 2

70 |
71 |
72 |

Slide 3

73 |
74 |
75 |

Slide 4

76 |
77 |
78 |

Slide 5

79 |
80 | 81 |
82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /example/sw-broadcast/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | #scroll-stage { 6 | height: 5000px; 7 | } 8 | .slide { 9 | width: 100%; 10 | height: 100%; 11 | position: fixed; 12 | z-index: 1; 13 | left: 0; 14 | top: 0; 15 | color: white; 16 | } 17 | .slide h2 { 18 | -webkit-transform: translate(-50%, -50%); 19 | -moz-transform: translate(-50%, -50%); 20 | -ms-transform: translate(-50%, -50%); 21 | -o-transform: translate(-50%, -50%); 22 | transform: translate(-50%, -50%); 23 | position: fixed; 24 | top: 50%; 25 | left: 50%; 26 | font-size: 80px; 27 | } 28 | .slide.ng-hide-add, 29 | .slide.ng-hide-remove { 30 | display: block !important; 31 | } 32 | .slide--1 { 33 | background-color: #3498db; 34 | } 35 | .slide--1.ng-hide-add { 36 | -webkit-animation: bounceOut 1s both; 37 | -moz-animation: bounceOut 1s both; 38 | -o-animation: bounceOut 1s both; 39 | animation: bounceOut 1s both; 40 | } 41 | .slide--1.ng-hide-remove { 42 | -webkit-animation: bounceIn 1s 300ms both; 43 | -moz-animation: bounceIn 1s 300ms both; 44 | -o-animation: bounceIn 1s 300ms both; 45 | animation: bounceIn 1s 300ms both; 46 | z-index: 2; 47 | } 48 | .slide--2 { 49 | background-color: #e74c3c; 50 | } 51 | .slide--2.ng-hide-add { 52 | -webkit-animation: zoomOutRight 1s both; 53 | -moz-animation: zoomOutRight 1s both; 54 | -o-animation: zoomOutRight 1s both; 55 | animation: zoomOutRight 1s both; 56 | } 57 | .slide--2.ng-hide-remove { 58 | -webkit-animation: zoomInLeft 1s 300ms both; 59 | -moz-animation: zoomInLeft 1s 300ms both; 60 | -o-animation: zoomInLeft 1s 300ms both; 61 | animation: zoomInLeft 1s 300ms both; 62 | z-index: 2; 63 | } 64 | .slide--3 { 65 | background-color: #34495e; 66 | } 67 | .slide--3.ng-hide-add { 68 | -webkit-animation: fadeOutLeft 1s both; 69 | -moz-animation: fadeOutLeft 1s both; 70 | -o-animation: fadeOutLeft 1s both; 71 | animation: fadeOutLeft 1s both; 72 | } 73 | .slide--3.ng-hide-remove { 74 | -webkit-animation: fadeInRight 1s 300ms both; 75 | -moz-animation: fadeInRight 1s 300ms both; 76 | -o-animation: fadeInRight 1s 300ms both; 77 | animation: fadeInRight 1s 300ms both; 78 | z-index: 2; 79 | } 80 | .slide--4 { 81 | background-color: #1abc9c; 82 | } 83 | .slide--4.ng-hide-add { 84 | -webkit-animation: bounceOutDown 1s both; 85 | -moz-animation: bounceOutDown 1s both; 86 | -o-animation: bounceOutDown 1s both; 87 | animation: bounceOutDown 1s both; 88 | } 89 | .slide--4.ng-hide-remove { 90 | -webkit-animation: bounceInDown 1s 300ms both; 91 | -moz-animation: bounceInDown 1s 300ms both; 92 | -o-animation: bounceInDown 1s 300ms both; 93 | animation: bounceInDown 1s 300ms both; 94 | z-index: 2; 95 | } 96 | .slide--5 { 97 | background-color: #ecf0f1; 98 | color: #333; 99 | } 100 | .slide--5.ng-hide-add { 101 | -webkit-animation: zoomOutUp 1s both; 102 | -moz-animation: zoomOutUp 1s both; 103 | -o-animation: zoomOutUp 1s both; 104 | animation: zoomOutUp 1s both; 105 | } 106 | .slide--5.ng-hide-remove { 107 | -webkit-animation: zoomInUp 1s 300ms both; 108 | -moz-animation: zoomInUp 1s 300ms both; 109 | -o-animation: zoomInUp 1s 300ms both; 110 | animation: zoomInUp 1s 300ms both; 111 | z-index: 2; 112 | } 113 | -------------------------------------------------------------------------------- /example/sw-broadcast/style.less: -------------------------------------------------------------------------------- 1 | @import "../assets/lesshat-prefixed"; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | #scroll-stage { 9 | height: 5000px; 10 | } 11 | 12 | .slide { 13 | .lh-size(100%); 14 | 15 | position: fixed; z-index: 1; 16 | left: 0; 17 | top: 0; 18 | 19 | color: white; 20 | 21 | h2 { 22 | .lh-translate(-50%, -50%); 23 | 24 | position: fixed; 25 | top: 50%; 26 | left: 50%; 27 | 28 | font-size: 80px; 29 | } 30 | 31 | &.ng-hide-add, &.ng-hide-remove { 32 | display: block !important; 33 | } 34 | 35 | .setup-anim (@show; @hide) { 36 | &.ng-hide-add { 37 | .lh-animation(@hide 1s both); 38 | } 39 | &.ng-hide-remove { 40 | .lh-animation(@show 1s 300ms both); 41 | z-index: 2; 42 | } 43 | } 44 | 45 | &--1 { 46 | .setup-anim(bounceIn; bounceOut); 47 | background-color: #3498db; 48 | } 49 | &--2 { 50 | .setup-anim(zoomInLeft; zoomOutRight); 51 | background-color: #e74c3c; 52 | } 53 | &--3 { 54 | .setup-anim(fadeInRight; fadeOutLeft); 55 | background-color: #34495e; 56 | } 57 | &--4 { 58 | .setup-anim(bounceInDown; bounceOutDown); 59 | background-color: #1abc9c; 60 | } 61 | &--5 { 62 | .setup-anim(zoomInUp; zoomOutUp); 63 | background-color: #ecf0f1; 64 | color: #333; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /example/sw-class-with-ng-repeat/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | angular-scroll-watch: sw-class with ng-repeat 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 74 | 75 | 76 | 77 | 78 | 79 | Fork me on GitHub 80 | 81 | 82 | 83 |
84 | 85 | 87 | 88 |
89 | 90 |
91 |
100 | 101 | angularjs shield 106 |
107 |
108 | 109 |
110 |

How much angular do you want?

111 | 112 |
113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /example/sw-class-with-ng-repeat/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | #scroll-stage { 6 | background-color: #333; 7 | height: 10000px; 8 | } 9 | #form { 10 | -webkit-transform: translate(-50%, -50%); 11 | -moz-transform: translate(-50%, -50%); 12 | -ms-transform: translate(-50%, -50%); 13 | -o-transform: translate(-50%, -50%); 14 | transform: translate(-50%, -50%); 15 | width: 600px; 16 | height: 230px; 17 | -webkit-border-radius: 5px; 18 | -webkit-background-clip: padding-box; 19 | -moz-border-radius: 5px; 20 | -moz-background-clip: padding; 21 | border-radius: 5px; 22 | background-clip: padding-box; 23 | position: fixed; 24 | top: 50%; 25 | left: 50%; 26 | z-index: 10; 27 | text-align: center; 28 | padding: 0 30px; 29 | background-color: rgba(177, 161, 120, 0.42); 30 | } 31 | #form h2 { 32 | margin-bottom: 20px; 33 | color: #ffd16e; 34 | text-shadow: 0 1px 1px #553e03; 35 | } 36 | #form input { 37 | height: 120px; 38 | font-size: 80px; 39 | text-align: center; 40 | } 41 | #angular-container { 42 | width: 100%; 43 | height: 100%; 44 | position: fixed; 45 | top: 0; 46 | left: 0; 47 | z-index: 5; 48 | } 49 | #angular-container .angular { 50 | position: absolute; 51 | } 52 | #angular-container .angular img { 53 | opacity: 0.8; 54 | } 55 | #angular-container .anim-1 { 56 | display: none; 57 | } 58 | #angular-container .anim-1.in-view { 59 | display: block; 60 | } 61 | #angular-container .anim-1.in-view-add { 62 | -webkit-animation: bounceInDown 1s both; 63 | -moz-animation: bounceInDown 1s both; 64 | -o-animation: bounceInDown 1s both; 65 | animation: bounceInDown 1s both; 66 | } 67 | #angular-container .anim-1.in-view-remove { 68 | -webkit-animation: bounceOutDown 1s both; 69 | -moz-animation: bounceOutDown 1s both; 70 | -o-animation: bounceOutDown 1s both; 71 | animation: bounceOutDown 1s both; 72 | display: block; 73 | } 74 | #angular-container .anim-2 { 75 | display: none; 76 | } 77 | #angular-container .anim-2.in-view { 78 | display: block; 79 | } 80 | #angular-container .anim-2.in-view-add { 81 | -webkit-animation: bounceInRight 1s both; 82 | -moz-animation: bounceInRight 1s both; 83 | -o-animation: bounceInRight 1s both; 84 | animation: bounceInRight 1s both; 85 | } 86 | #angular-container .anim-2.in-view-remove { 87 | -webkit-animation: bounceOutLeft 1s both; 88 | -moz-animation: bounceOutLeft 1s both; 89 | -o-animation: bounceOutLeft 1s both; 90 | animation: bounceOutLeft 1s both; 91 | display: block; 92 | } 93 | #angular-container .anim-3 { 94 | display: none; 95 | } 96 | #angular-container .anim-3.in-view { 97 | display: block; 98 | } 99 | #angular-container .anim-3.in-view-add { 100 | -webkit-animation: bounceInUp 1s both; 101 | -moz-animation: bounceInUp 1s both; 102 | -o-animation: bounceInUp 1s both; 103 | animation: bounceInUp 1s both; 104 | } 105 | #angular-container .anim-3.in-view-remove { 106 | -webkit-animation: bounceOutUp 1s both; 107 | -moz-animation: bounceOutUp 1s both; 108 | -o-animation: bounceOutUp 1s both; 109 | animation: bounceOutUp 1s both; 110 | display: block; 111 | } 112 | #angular-container .anim-4 { 113 | display: none; 114 | } 115 | #angular-container .anim-4.in-view { 116 | display: block; 117 | } 118 | #angular-container .anim-4.in-view-add { 119 | -webkit-animation: fadeInDown 1s both; 120 | -moz-animation: fadeInDown 1s both; 121 | -o-animation: fadeInDown 1s both; 122 | animation: fadeInDown 1s both; 123 | } 124 | #angular-container .anim-4.in-view-remove { 125 | -webkit-animation: fadeOutDown 1s both; 126 | -moz-animation: fadeOutDown 1s both; 127 | -o-animation: fadeOutDown 1s both; 128 | animation: fadeOutDown 1s both; 129 | display: block; 130 | } 131 | #angular-container .anim-5 { 132 | display: none; 133 | } 134 | #angular-container .anim-5.in-view { 135 | display: block; 136 | } 137 | #angular-container .anim-5.in-view-add { 138 | -webkit-animation: fadeInRight 1s both; 139 | -moz-animation: fadeInRight 1s both; 140 | -o-animation: fadeInRight 1s both; 141 | animation: fadeInRight 1s both; 142 | } 143 | #angular-container .anim-5.in-view-remove { 144 | -webkit-animation: fadeOutLeft 1s both; 145 | -moz-animation: fadeOutLeft 1s both; 146 | -o-animation: fadeOutLeft 1s both; 147 | animation: fadeOutLeft 1s both; 148 | display: block; 149 | } 150 | #angular-container .anim-6 { 151 | display: none; 152 | } 153 | #angular-container .anim-6.in-view { 154 | display: block; 155 | } 156 | #angular-container .anim-6.in-view-add { 157 | -webkit-animation: fadeInUp 1s both; 158 | -moz-animation: fadeInUp 1s both; 159 | -o-animation: fadeInUp 1s both; 160 | animation: fadeInUp 1s both; 161 | } 162 | #angular-container .anim-6.in-view-remove { 163 | -webkit-animation: fadeOutUp 1s both; 164 | -moz-animation: fadeOutUp 1s both; 165 | -o-animation: fadeOutUp 1s both; 166 | animation: fadeOutUp 1s both; 167 | display: block; 168 | } 169 | #angular-container .anim-7 { 170 | display: none; 171 | } 172 | #angular-container .anim-7.in-view { 173 | display: block; 174 | } 175 | #angular-container .anim-7.in-view-add { 176 | -webkit-animation: rotateIn 1s both; 177 | -moz-animation: rotateIn 1s both; 178 | -o-animation: rotateIn 1s both; 179 | animation: rotateIn 1s both; 180 | } 181 | #angular-container .anim-7.in-view-remove { 182 | -webkit-animation: rotateOut 1s both; 183 | -moz-animation: rotateOut 1s both; 184 | -o-animation: rotateOut 1s both; 185 | animation: rotateOut 1s both; 186 | display: block; 187 | } 188 | #angular-container .anim-8 { 189 | display: none; 190 | } 191 | #angular-container .anim-8.in-view { 192 | display: block; 193 | } 194 | #angular-container .anim-8.in-view-add { 195 | -webkit-animation: flipInX 1s both; 196 | -moz-animation: flipInX 1s both; 197 | -o-animation: flipInX 1s both; 198 | animation: flipInX 1s both; 199 | } 200 | #angular-container .anim-8.in-view-remove { 201 | -webkit-animation: flipOutX 1s both; 202 | -moz-animation: flipOutX 1s both; 203 | -o-animation: flipOutX 1s both; 204 | animation: flipOutX 1s both; 205 | display: block; 206 | } 207 | #angular-container .anim-9 { 208 | display: none; 209 | } 210 | #angular-container .anim-9.in-view { 211 | display: block; 212 | } 213 | #angular-container .anim-9.in-view-add { 214 | -webkit-animation: flipInY 1s both; 215 | -moz-animation: flipInY 1s both; 216 | -o-animation: flipInY 1s both; 217 | animation: flipInY 1s both; 218 | } 219 | #angular-container .anim-9.in-view-remove { 220 | -webkit-animation: flipOutY 1s both; 221 | -moz-animation: flipOutY 1s both; 222 | -o-animation: flipOutY 1s both; 223 | animation: flipOutY 1s both; 224 | display: block; 225 | } 226 | #angular-container .anim-10 { 227 | display: none; 228 | } 229 | #angular-container .anim-10.in-view { 230 | display: block; 231 | } 232 | #angular-container .anim-10.in-view-add { 233 | -webkit-animation: zoomInDown 1s both; 234 | -moz-animation: zoomInDown 1s both; 235 | -o-animation: zoomInDown 1s both; 236 | animation: zoomInDown 1s both; 237 | } 238 | #angular-container .anim-10.in-view-remove { 239 | -webkit-animation: zoomOutDown 1s both; 240 | -moz-animation: zoomOutDown 1s both; 241 | -o-animation: zoomOutDown 1s both; 242 | animation: zoomOutDown 1s both; 243 | display: block; 244 | } 245 | #angular-container .anim-11 { 246 | display: none; 247 | } 248 | #angular-container .anim-11.in-view { 249 | display: block; 250 | } 251 | #angular-container .anim-11.in-view-add { 252 | -webkit-animation: zoomInRight 1s both; 253 | -moz-animation: zoomInRight 1s both; 254 | -o-animation: zoomInRight 1s both; 255 | animation: zoomInRight 1s both; 256 | } 257 | #angular-container .anim-11.in-view-remove { 258 | -webkit-animation: zoomOutLeft 1s both; 259 | -moz-animation: zoomOutLeft 1s both; 260 | -o-animation: zoomOutLeft 1s both; 261 | animation: zoomOutLeft 1s both; 262 | display: block; 263 | } 264 | #angular-container .anim-12 { 265 | display: none; 266 | } 267 | #angular-container .anim-12.in-view { 268 | display: block; 269 | } 270 | #angular-container .anim-12.in-view-add { 271 | -webkit-animation: zoomInUp 1s both; 272 | -moz-animation: zoomInUp 1s both; 273 | -o-animation: zoomInUp 1s both; 274 | animation: zoomInUp 1s both; 275 | } 276 | #angular-container .anim-12.in-view-remove { 277 | -webkit-animation: zoomOutUp 1s both; 278 | -moz-animation: zoomOutUp 1s both; 279 | -o-animation: zoomOutUp 1s both; 280 | animation: zoomOutUp 1s both; 281 | display: block; 282 | } 283 | #angular-container .anim-13 { 284 | display: none; 285 | } 286 | #angular-container .anim-13.in-view { 287 | display: block; 288 | } 289 | #angular-container .anim-13.in-view-add { 290 | -webkit-animation: rollIn 1s both; 291 | -moz-animation: rollIn 1s both; 292 | -o-animation: rollIn 1s both; 293 | animation: rollIn 1s both; 294 | } 295 | #angular-container .anim-13.in-view-remove { 296 | -webkit-animation: rollOut 1s both; 297 | -moz-animation: rollOut 1s both; 298 | -o-animation: rollOut 1s both; 299 | animation: rollOut 1s both; 300 | display: block; 301 | } 302 | -------------------------------------------------------------------------------- /example/sw-class-with-ng-repeat/style.less: -------------------------------------------------------------------------------- 1 | @import "../assets/lesshat-prefixed"; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | #scroll-stage { 9 | background-color: #333; 10 | height: 10000px; 11 | } 12 | 13 | #form { 14 | .lh-translate(-50%, -50%); 15 | .lh-size(600px, 230px); 16 | .lh-border-radius(5px); 17 | 18 | position: fixed; 19 | top: 50%; 20 | left: 50%; 21 | z-index: 10; 22 | 23 | text-align: center; 24 | 25 | padding: 0 30px; 26 | background-color: rgba(177, 161, 120, 0.42); 27 | 28 | h2 { 29 | margin-bottom: 20px; 30 | color: rgb(255, 209, 110); 31 | text-shadow: 0 1px 1px rgb(85, 62, 3); 32 | } 33 | 34 | input { 35 | @font-size: 80px; 36 | @line-height: 1.5; 37 | height: @font-size * @line-height; 38 | font-size: @font-size; 39 | text-align: center; 40 | } 41 | } 42 | 43 | #angular-container { 44 | .lh-size(100%); 45 | 46 | position: fixed; 47 | top: 0; left: 0; 48 | z-index: 5; 49 | 50 | .angular { 51 | position: absolute; 52 | 53 | img { 54 | opacity: 0.8; 55 | } 56 | } 57 | 58 | .setup-anim (@no; @add-anim; @remove-anim) { 59 | .anim-@{no} { 60 | display: none; 61 | 62 | &.in-view { 63 | display: block; 64 | 65 | &-add { 66 | .lh-animation(@add-anim 1s both); 67 | } 68 | &-remove { 69 | .lh-animation(@remove-anim 1s both); 70 | display: block; 71 | } 72 | } 73 | } 74 | } 75 | 76 | .setup-anim(1; bounceInDown; bounceOutDown); 77 | .setup-anim(2; bounceInRight; bounceOutLeft); 78 | .setup-anim(3; bounceInUp; bounceOutUp); 79 | 80 | .setup-anim(4; fadeInDown; fadeOutDown); 81 | .setup-anim(5; fadeInRight; fadeOutLeft); 82 | .setup-anim(6; fadeInUp; fadeOutUp); 83 | 84 | .setup-anim(7; rotateIn; rotateOut); 85 | 86 | .setup-anim(8; flipInX; flipOutX); 87 | .setup-anim(9; flipInY; flipOutY); 88 | 89 | .setup-anim(10; zoomInDown; zoomOutDown); 90 | .setup-anim(11; zoomInRight; zoomOutLeft); 91 | .setup-anim(12; zoomInUp; zoomOutUp); 92 | 93 | .setup-anim(13; rollIn; rollOut); 94 | } 95 | -------------------------------------------------------------------------------- /example/sw-class/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | angular-scroll-watch: sw-class 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 32 | 33 | 34 | 35 | 36 | 37 | Fork me on GitHub 38 | 39 | 40 | 41 |
42 | 43 | 45 | 46 |
49 |
50 | 51 |

52 |
Stay with me!!
60 |

61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /example/sw-class/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | #scroll-stage { 6 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNmYWM2OTUiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iNDclIiBzdG9wLWNvbG9yPSIjZjVhYjY2IiBzdG9wLW9wYWNpdHk9IjEiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNlZjhkMzEiIHN0b3Atb3BhY2l0eT0iMSIvPjwvbGluZWFyR3JhZGllbnQ+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNsZXNzaGF0LWdlbmVyYXRlZCkiIC8+PC9zdmc+); 7 | background-image: -webkit-linear-gradient(0deg, #fac695 0, #f5ab66 47%, #ef8d31 100%); 8 | background-image: -moz-linear-gradient(0deg, #fac695 0, #f5ab66 47%, #ef8d31 100%); 9 | background-image: -o-linear-gradient(0deg, #fac695 0, #f5ab66 47%, #ef8d31 100%); 10 | background-image: linear-gradient(90deg, #fac695 0, #f5ab66 47%, #ef8d31 100%); 11 | -webkit-background-size: auto auto; 12 | -moz-background-size: auto auto; 13 | background-size: auto auto; 14 | -webkit-background-origin: padding-box; 15 | -moz-background-origin: padding-box; 16 | background-origin: padding-box; 17 | -webkit-background-clip: border-box; 18 | -moz-background-clip: border-box; 19 | background-clip: border-box; 20 | height: 5000px; 21 | } 22 | #hero { 23 | -webkit-transform: translate(-50%, -50%); 24 | -moz-transform: translate(-50%, -50%); 25 | -ms-transform: translate(-50%, -50%); 26 | -o-transform: translate(-50%, -50%); 27 | transform: translate(-50%, -50%); 28 | position: fixed; 29 | top: 50%; 30 | left: 50%; 31 | } 32 | .p20 { 33 | font-size: 20px; 34 | } 35 | .p20-add { 36 | -webkit-animation: shake 1s; 37 | -moz-animation: shake 1s; 38 | -o-animation: shake 1s; 39 | animation: shake 1s; 40 | font-size: 20px; 41 | } 42 | .p20-remove { 43 | -webkit-transition: all 500ms ease-in-out; 44 | -moz-transition: all 500ms ease-in-out; 45 | -o-transition: all 500ms ease-in-out; 46 | transition: all 500ms ease-in-out; 47 | } 48 | .p40 { 49 | font-size: 60px; 50 | } 51 | .p40-add { 52 | -webkit-animation: tada 1s; 53 | -moz-animation: tada 1s; 54 | -o-animation: tada 1s; 55 | animation: tada 1s; 56 | font-size: 60px; 57 | } 58 | .p40-remove { 59 | -webkit-transition: all 500ms ease-in-out; 60 | -moz-transition: all 500ms ease-in-out; 61 | -o-transition: all 500ms ease-in-out; 62 | transition: all 500ms ease-in-out; 63 | } 64 | .p80 { 65 | font-size: 100px; 66 | } 67 | .p80-add { 68 | -webkit-animation: rubberBand 1s; 69 | -moz-animation: rubberBand 1s; 70 | -o-animation: rubberBand 1s; 71 | animation: rubberBand 1s; 72 | font-size: 100px; 73 | } 74 | .p80-remove { 75 | -webkit-transition: all 500ms ease-in-out; 76 | -moz-transition: all 500ms ease-in-out; 77 | -o-transition: all 500ms ease-in-out; 78 | transition: all 500ms ease-in-out; 79 | } 80 | .p100 { 81 | font-size: 140px; 82 | } 83 | .p100-add { 84 | -webkit-animation: flip 1s; 85 | -moz-animation: flip 1s; 86 | -o-animation: flip 1s; 87 | animation: flip 1s; 88 | font-size: 140px; 89 | } 90 | .p100-remove { 91 | -webkit-transition: all 500ms ease-in-out; 92 | -moz-transition: all 500ms ease-in-out; 93 | -o-transition: all 500ms ease-in-out; 94 | transition: all 500ms ease-in-out; 95 | } 96 | -------------------------------------------------------------------------------- /example/sw-class/style.less: -------------------------------------------------------------------------------- 1 | @import "../assets/lesshat-prefixed"; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | #scroll-stage { 9 | .lh-background-image(linear-gradient(90deg, rgba(250,198,149,1) 0, rgba(245,171,102,1) 47%, rgba(239,141,49,1) 100%)); 10 | .lh-background-size(auto, auto); 11 | .lh-background-origin(padding-box); 12 | .lh-background-clip(border-box); 13 | 14 | height: 5000px; 15 | } 16 | 17 | #hero { 18 | .lh-translate(-50%, -50%); 19 | 20 | position: fixed; 21 | top: 50%; 22 | left: 50%; 23 | } 24 | 25 | .make-progress(@font-size; @animation) { 26 | font-size: @font-size; 27 | 28 | &-add { 29 | .lh-animation(@animation); 30 | font-size: @font-size; 31 | } 32 | &-remove { 33 | .lh-transition(all 500ms ease-in-out); 34 | } 35 | } 36 | 37 | .p20 { 38 | .make-progress(20px; shake 1s); 39 | } 40 | 41 | .p40 { 42 | .make-progress(60px; tada 1s); 43 | } 44 | 45 | .p80 { 46 | .make-progress(100px; rubberBand 1s); 47 | } 48 | 49 | .p100 { 50 | .make-progress(140px; flip 1s); 51 | } 52 | -------------------------------------------------------------------------------- /example/sw-stage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | angular-scroll-watch: sw-stage 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 52 | 53 | 54 | 55 | 56 | 57 | Fork me on GitHub 58 | 59 | 60 | 61 |
62 | 63 | 65 | 66 | 67 |
68 |
74 | 75 | angularjs shield 87 |
88 | 89 |
90 |
91 |
92 | 93 |
94 |
95 |
96 |
97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /example/sw-stage/style.css: -------------------------------------------------------------------------------- 1 | lesshat-selector { 2 | -lh-property: 0; } 3 | @-webkit-keyframes rotating{ from{-webkit-transform: rotateY(0);} to{-webkit-transform: rotateY(360deg);}} 4 | @-moz-keyframes rotating{ from{-moz-transform: rotateY(0);} to{-moz-transform: rotateY(360deg);}} 5 | @-o-keyframes rotating{ from{-o-transform: rotateY(0);} to{-o-transform: rotateY(360deg);}} 6 | @keyframes rotating{ from{-webkit-transform: rotateY(0);-moz-transform: rotateY(0);-ms-transform: rotateY(0);transform: rotateY(0);} to{-webkit-transform: rotateY(360deg);-moz-transform: rotateY(360deg);-ms-transform: rotateY(360deg);transform: rotateY(360deg);}; 7 | } 8 | body { 9 | margin: 0; 10 | padding: 0; 11 | } 12 | #wrap { 13 | -webkit-transform: translate(-50%, -50%); 14 | -moz-transform: translate(-50%, -50%); 15 | -ms-transform: translate(-50%, -50%); 16 | -o-transform: translate(-50%, -50%); 17 | transform: translate(-50%, -50%); 18 | position: fixed; 19 | top: 50%; 20 | left: 50%; 21 | height: 60%; 22 | } 23 | .speed-control, 24 | .size-control { 25 | position: absolute; 26 | left: -170px; 27 | top: 0; 28 | width: 120px; 29 | height: 100%; 30 | max-height: 415px; 31 | overflow-y: auto; 32 | background-color: rgba(0, 0, 0, 0.3); 33 | } 34 | .speed-control__height, 35 | .size-control__height { 36 | height: 400%; 37 | } 38 | .speed-control:hover, 39 | .size-control:hover { 40 | background-color: rgba(0, 0, 0, 0.5); 41 | } 42 | .size-control { 43 | left: auto; 44 | right: -170px; 45 | } 46 | .shield-holder { 47 | -webkit-perspective: 400px; 48 | -moz-perspective: 400px; 49 | perspective: 400px; 50 | } 51 | .shield-holder img { 52 | display: block; 53 | } 54 | .shield-holder img.anim { 55 | -webkit-animation: rotating 4s linear both infinite; 56 | -moz-animation: rotating 4s linear both infinite; 57 | -o-animation: rotating 4s linear both infinite; 58 | animation: rotating 4s linear both infinite; 59 | } 60 | .shield-holder img.anim.paused { 61 | -webkit-animation-play-state: paused; 62 | -moz-animation-play-state: paused; 63 | -o-animation-play-state: paused; 64 | animation-play-state: paused; 65 | } 66 | -------------------------------------------------------------------------------- /example/sw-stage/style.less: -------------------------------------------------------------------------------- 1 | @import "../assets/lesshat-prefixed"; 2 | 3 | .lh-keyframes(~"rotating, from{transform: rotateY(0);} to{transform: rotateY(360deg);}"); 4 | 5 | @control-width: 120px; 6 | @control-offset: 50px; 7 | 8 | body { 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | #wrap { 14 | .lh-translate(-50%, -50%); 15 | 16 | position: fixed; 17 | top: 50%; 18 | left: 50%; 19 | 20 | height: 60%; 21 | } 22 | 23 | .speed-control { 24 | position: absolute; 25 | left: -(@control-width + @control-offset); top: 0; 26 | 27 | width: @control-width; 28 | height: 100%; 29 | max-height: 415px; 30 | 31 | overflow-y: auto; 32 | 33 | background-color: rgba(0, 0, 0, 0.3); 34 | 35 | &__height { 36 | height: 400%; 37 | } 38 | 39 | &:hover { 40 | background-color: rgba(0, 0, 0, 0.5); 41 | } 42 | } 43 | 44 | .size-control { 45 | &:extend(.speed-control all); 46 | 47 | left: auto; 48 | right: -(@control-width + @control-offset); 49 | } 50 | 51 | 52 | .shield-holder { 53 | .lh-perspective(400); 54 | 55 | img { 56 | display: block; 57 | &.anim { 58 | .lh-animation(rotating 4s linear both infinite); 59 | 60 | &.paused { 61 | .lh-animation-play-state(paused); 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /example/sw-style-touch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | angular-scroll-watch: sw-style touch-enabled 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 39 | 40 | 41 | 42 | 43 | 44 | Fork me on GitHub 45 | 46 | 47 | 48 |
49 | 50 | 52 | 53 |
54 |
57 |
58 | 59 |

60 |
YA!!
69 |

70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /example/sw-style-touch/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | #touch-wrap { 6 | width: 100%; 7 | height: 100vh; 8 | overflow-y: auto; 9 | } 10 | #scroll-stage { 11 | height: 150vh; 12 | } 13 | #hero { 14 | -webkit-transform: translate(-50%, -50%); 15 | -moz-transform: translate(-50%, -50%); 16 | -ms-transform: translate(-50%, -50%); 17 | -o-transform: translate(-50%, -50%); 18 | transform: translate(-50%, -50%); 19 | -webkit-perspective: 400px; 20 | -moz-perspective: 400px; 21 | perspective: 400px; 22 | position: fixed; 23 | top: 50%; 24 | left: 50%; 25 | pointer-events: none; 26 | } 27 | #hero .text { 28 | font-size: 200%; 29 | } 30 | -------------------------------------------------------------------------------- /example/sw-style-touch/style.less: -------------------------------------------------------------------------------- 1 | @import "../assets/lesshat-prefixed"; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | #touch-wrap { 9 | width: 100%; 10 | height: 100vh; 11 | 12 | overflow-y: auto; 13 | } 14 | 15 | #scroll-stage { 16 | height: 150vh; 17 | } 18 | 19 | #hero { 20 | .lh-translate(-50%, -50%); 21 | .lh-perspective(400); 22 | 23 | position: fixed; 24 | top: 50%; 25 | left: 50%; 26 | 27 | pointer-events: none; 28 | 29 | .text { 30 | font-size: 200%; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example/sw-style/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | angular-scroll-watch: sw-style 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 37 | 38 | 39 | 40 | 41 | 42 | Fork me on GitHub 43 | 44 | 45 | 46 |
47 | 48 | 50 | 51 |
54 |
55 | 56 |

62 | Stay with me!! 63 |

64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /example/sw-style/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | #scroll-stage { 6 | height: 5000px; 7 | } 8 | #hero { 9 | -webkit-transform: translate(-50%, -50%); 10 | -moz-transform: translate(-50%, -50%); 11 | -ms-transform: translate(-50%, -50%); 12 | -o-transform: translate(-50%, -50%); 13 | transform: translate(-50%, -50%); 14 | position: fixed; 15 | top: 50%; 16 | left: 50%; 17 | } 18 | -------------------------------------------------------------------------------- /example/sw-style/style.less: -------------------------------------------------------------------------------- 1 | @import "../assets/lesshat-prefixed"; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | #scroll-stage { 9 | height: 5000px; 10 | } 11 | 12 | #hero { 13 | .lh-translate(-50%, -50%); 14 | 15 | position: fixed; 16 | top: 50%; 17 | left: 50%; 18 | } 19 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /*global require*/ 2 | var gulp = require('gulp'), 3 | gutil = require('gulp-util'); 4 | 5 | var connect = require('gulp-connect'), 6 | livereload = require('gulp-livereload'), 7 | uglify = require('gulp-uglify'), 8 | jshint = require('gulp-jshint'), 9 | changed = require('gulp-changed'), 10 | cache = require('gulp-cached'), 11 | rename = require('gulp-rename'), 12 | annotate = require('gulp-ng-annotate'), 13 | header = require('gulp-header'), 14 | less = require('gulp-less'); 15 | 16 | var pkg = require('./package.json'); 17 | 18 | var getTodayStr = function () { 19 | var date = new Date(), 20 | y = date.getFullYear(), 21 | m = date.getMonth() + 1, 22 | d = date.getDate(); 23 | 24 | return y + '-' + m + '-' + d; 25 | }; 26 | 27 | var config = { 28 | appRoot: '', 29 | src: 'src/angular-scroll-watch.js', 30 | buildDir: 'build', 31 | banner: '/*! <%= pkg.name %>\n' + 32 | 'version: <%= pkg.version %>\n' + 33 | 'build date: <%= today %>\n' + 34 | 'author: <%= pkg.author %>\n' + 35 | '<%= pkg.repository.url %> */\n' 36 | }; 37 | 38 | gulp.task('build', ['lint'], function () { 39 | return gulp.src(config.src) 40 | .pipe(annotate()) 41 | .pipe(header(config.banner, {pkg: pkg, today: getTodayStr()})) 42 | .pipe(gulp.dest(config.buildDir)) 43 | .pipe(uglify()) 44 | .pipe(rename({extname: '.min.js'})) 45 | .pipe(header(config.banner, {pkg: pkg, today: getTodayStr()})) 46 | .pipe(gulp.dest(config.buildDir)); 47 | }); 48 | gulp.task('lint', function () { 49 | return gulp.src(config.src) 50 | .pipe(jshint()) 51 | .pipe(jshint.reporter('jshint-stylish')); 52 | }); 53 | gulp.task('watch', function () { 54 | gulp.watch(config.src, ['build']); 55 | }); 56 | 57 | 58 | gulp.task('server', function () { 59 | connect.server({ 60 | root: config.appRoot, 61 | port: 9000 62 | }); 63 | }); 64 | gulp.task('watch:example', function () { 65 | livereload.listen(); 66 | 67 | var paths = [ 68 | config.src, 69 | 'example/{,**/}*.{js,html,css}', 70 | 'example/*/*.less' 71 | ]; 72 | gulp.watch(paths, ['lint:example', 'less:example']) 73 | .on('change', livereload.changed); 74 | }); 75 | gulp.task('lint:example', function () { 76 | return gulp.src('example/{,**/}*.{html,js}', {base: './'}) 77 | .pipe(cache('lint-example')) 78 | .pipe(jshint.extract('auto')) 79 | .pipe(jshint()) 80 | .pipe(jshint.reporter('jshint-stylish')); 81 | }); 82 | gulp.task('less:example', function () { 83 | return gulp.src('example/*/style.less', {base: './'}) 84 | .pipe(changed('./', {extension: '.css'})) 85 | .pipe(less()) 86 | .pipe(gulp.dest('./')); 87 | }); 88 | 89 | gulp.task('default', ['build']); 90 | gulp.task('example', ['server', 'watch', 'watch:example']); 91 | -------------------------------------------------------------------------------- /images/how_much_angular_do_you_want.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pc035860/angular-scroll-watch/74f98efe97c5a8ea7f160cd027c60d2d05191e85/images/how_much_angular_do_you_want.gif -------------------------------------------------------------------------------- /images/lovely_slides.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pc035860/angular-scroll-watch/74f98efe97c5a8ea7f160cd027c60d2d05191e85/images/lovely_slides.gif -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-scroll-watch", 3 | "version": "0.6.1", 4 | "description": "Basic scroll-aware directives for AngularJS.", 5 | "main": "build/angular-scroll-watch.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/pc035860/angular-scroll-watch.git" 12 | }, 13 | "keywords": [ 14 | "angularjs", 15 | "scroll" 16 | ], 17 | "author": { 18 | "name": "Chih-Hsuan Fan", 19 | "email": "pc035860@gmail.com" 20 | }, 21 | "readmeFilename": "README.md", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/pc035860/angular-scroll-watch/issues" 25 | }, 26 | "homepage": "https://github.com/pc035860/angular-scroll-watch", 27 | "dependencies": { 28 | "angular": "^1.2" 29 | }, 30 | "devDependencies": { 31 | "gulp": "^3.8.6", 32 | "gulp-util": "^3.0.0", 33 | "gulp-connect": "^2.0.6", 34 | "gulp-livereload": "^2.1.0", 35 | "jshint-stylish": "^0.4.0", 36 | "gulp-jshint": "^1.8.4", 37 | "gulp-uglify": "^0.3.1", 38 | "gulp-changed": "^1.0.0", 39 | "gulp-rename": "^1.2.0", 40 | "gulp-ng-annotate": "^0.3.0", 41 | "gulp-header": "^1.0.5", 42 | "gulp-less": "^1.3.5", 43 | "gulp-cached": "^1.0.1" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/angular-scroll-watch.js: -------------------------------------------------------------------------------- 1 | /* jshint node:true */ 2 | /* global define */ 3 | (function (root, factory) { 4 | if (typeof exports === "object" || (typeof module === "object" && module.exports)) { 5 | module.exports = factory(require("angular")); 6 | } else if (typeof define === "function" && define.amd) { 7 | define(["angular"], factory); 8 | } else { 9 | root.returnExports = factory(root.angular); 10 | } 11 | }(this, function (angular) { 12 | 13 | var MODULE_NAME = 'pc035860.scrollWatch'; 14 | 15 | var DIR_STYLE = 'swStyle', 16 | DIR_CLASS = 'swClass', 17 | DIR_BROADCAST = 'swBroadcast', 18 | DIR_STAGE = 'swStage'; 19 | 20 | var STAGE_NAME_DEFAULT = 'pc035860'; 21 | 22 | var CACHE_ID_STAGE_POOL = 'scrollWatch.stages'; 23 | 24 | angular.module(MODULE_NAME, []) 25 | 26 | .factory('scrollWatchStageFactory', function ( 27 | $window, $document, $parse, $log, $rootScope, $animate 28 | ) { 29 | // ref: http://davidwalsh.name/function-debounce 30 | var debounce = function (func, wait, immediate) { 31 | var timeout; 32 | return function() { 33 | var context = this, args = arguments; 34 | $window.clearTimeout(timeout); 35 | timeout = $window.setTimeout(function() { 36 | timeout = null; 37 | if (!immediate) func.apply(context, args); 38 | }, wait); 39 | if (immediate && !timeout) func.apply(context, args); 40 | }; 41 | }; 42 | 43 | var requestAnimFrame = (function () { 44 | return $window.requestAnimationFrame || 45 | $window.webkitRequestAnimationFrame || 46 | $window.mozRequestAnimationFrame || 47 | $window.oRequestAnimationFrame || 48 | $window.msRequestAnimationFrame || 49 | function(/* function */ callback, /* DOMElement */ element){ 50 | $window.setTimeout(callback, 1000 / 60); 51 | }; 52 | })(); 53 | 54 | var $win = angular.element($window); 55 | 56 | /** 57 | * Stage class 58 | */ 59 | var Stage = function (name, $elm) { 60 | this.init(name, $elm); 61 | }; 62 | 63 | var p = Stage.prototype; 64 | 65 | p.name = null; 66 | p.element = null; 67 | p.configs = null; 68 | p._configId = 0; 69 | p._binded = false; 70 | 71 | p.init = function (name, $elm) { 72 | this.name = name; 73 | this.element = $elm || null; 74 | 75 | this.scrollHandler = this._digest.bind(this); 76 | this._digestDebounced = debounce(this._digest, 50); 77 | 78 | this._digestDebounced(); 79 | }; 80 | 81 | p.setElement = function ($elm) { 82 | this.element = $elm; 83 | 84 | if (this.configs !== null && !this._binded) { 85 | this._bind(this.element); 86 | } 87 | 88 | this._digestDebounced(); 89 | }; 90 | 91 | p.clearElement = function () { 92 | if (this._binded) { 93 | this._unbind(this.element); 94 | } 95 | this.element = null; 96 | }; 97 | 98 | p.addConfig = function (config) { 99 | angular.forEach(['target', 'from', 'to'], function (key) { 100 | if (angular.isUndefined(config[key])) { 101 | throw new Error('`'+ key +'` should be provided'); 102 | } 103 | }); 104 | 105 | if (this.configs === null) { 106 | this.configs = {}; 107 | 108 | if (this.element !== null) { 109 | this._bind(this.element); 110 | } 111 | } 112 | 113 | this._configId++; 114 | 115 | if (config.styleExpr) { 116 | config.styleGetter = $parse(config.styleExpr); 117 | } 118 | if (config.classExpr) { 119 | config.classGetter = $parse(config.classExpr); 120 | } 121 | if (config.brdcstExpr) { 122 | var buf = config.scope.$eval(config.brdcstExpr); 123 | 124 | if (buf.$rootScope) { 125 | config.brdcstScope = $rootScope; 126 | delete buf.$rootScope; 127 | } 128 | else { 129 | config.brdcstScope = config.scope; 130 | } 131 | 132 | if (buf.$emit) { 133 | config.brdcstIsEmit = true; 134 | delete buf.$emit; 135 | } 136 | else { 137 | config.brdcstIsEmit = false; 138 | } 139 | 140 | config.brdcstList = []; 141 | angular.forEach(buf, function (expr, event) { 142 | var pack = { 143 | event: event 144 | }; 145 | 146 | if (!angular.isString(expr)) { 147 | pack.always = true; 148 | } 149 | else { 150 | pack.condition = $parse(expr); 151 | pack.wasActive = null; 152 | } 153 | 154 | config.brdcstList.push(pack); 155 | }); 156 | } 157 | 158 | this.configs[this._configId] = config; 159 | 160 | this._digestDebounced(); 161 | 162 | return this._configId; 163 | }; 164 | 165 | p.removeConfig = function (configId) { 166 | if (this.configs && this.configs[configId]) { 167 | delete this.configs[configId]; 168 | 169 | if (objectSize(this.configs) === 0) { 170 | this.configs = null; 171 | 172 | if (this.element !== null) { 173 | this._unbind(this.element); 174 | } 175 | } 176 | } 177 | }; 178 | 179 | p.digest = function (configId) { 180 | this._digest(null, configId); 181 | }; 182 | 183 | p.destroy = function () { 184 | this.clearElement(); 185 | this.configs = null; 186 | this.scrollHandler = null; 187 | this._digestDebounced = null; 188 | }; 189 | 190 | p.couldDestroy = function () { 191 | return this.element === null && this.configs === null; 192 | }; 193 | 194 | p._isDefault = function () { 195 | return this.element[0] === $window; 196 | }; 197 | 198 | p._getElementMetrics = function ($elm) { 199 | var rect, metrics, scrollTop, elm, stageTop; 200 | 201 | elm = $elm[0]; 202 | rect = elm.getBoundingClientRect(); 203 | scrollTop = this._scrollTop(); 204 | 205 | metrics = { 206 | offsetTop: rect.top + scrollTop 207 | }; 208 | 209 | if (this._isDefault()) { 210 | stageTop = metrics.offsetTop; 211 | } 212 | else { 213 | stageTop = this._traverseStageTop(elm); 214 | } 215 | 216 | if (angular.isDefined(stageTop) && stageTop !== null) { 217 | metrics.stageTop = stageTop; 218 | } 219 | 220 | return metrics; 221 | }; 222 | 223 | p._traverseStageTop = function (elm) { 224 | var stageElm = this.element[0], 225 | top = 0, cursor, progress; 226 | 227 | var updateProgress = function (cursor, progress) { 228 | if (!progress) { 229 | progress = {}; 230 | } 231 | progress.parentNode = cursor.offsetParent; 232 | progress.offsetTop = cursor.offsetTop; 233 | progress.hitStage = progress.parentNode === stageElm; 234 | return progress; 235 | }; 236 | 237 | var c = 0; 238 | 239 | cursor = elm; 240 | progress = updateProgress(cursor); 241 | 242 | do { 243 | cursor = cursor.parentNode || cursor; 244 | 245 | if (progress.parentNode === cursor) { 246 | top += progress.offsetTop; 247 | 248 | if (progress.hitStage) { 249 | return top; 250 | } 251 | 252 | progress = updateProgress(cursor, progress); 253 | } 254 | else if (cursor === stageElm) { 255 | return top + progress.offsetTop - stageElm.offsetTop; 256 | } 257 | 258 | if (++c >= 10) { 259 | break; 260 | } 261 | } while (cursor.tagName !== 'BODY' || elm === cursor); 262 | 263 | return null; 264 | }; 265 | 266 | p._contentHeight = function () { 267 | if (this._isDefault()) { 268 | var doc = $document[0].documentElement, 269 | body = $document[0].body; 270 | 271 | return Math.max( 272 | body.scrollHeight, doc.scrollHeight, 273 | body.offsetHeight, doc.offsetHeight, 274 | doc.clientHeight 275 | ); 276 | } 277 | return this.element[0].scrollHeight; 278 | }; 279 | 280 | p._containerHeight = function () { 281 | if (this._isDefault()) { 282 | var h1 = $document[0].documentElement.clientHeight, 283 | h2 = $window.innerHeight; 284 | if (h1 > h2) { 285 | return h2; 286 | } 287 | return h1; 288 | } 289 | return this.element[0].offsetHeight; 290 | }; 291 | 292 | p._scrollTop = function () { 293 | if (this._isDefault()) { 294 | return $window.pageYOffset; 295 | } 296 | return this.element[0].scrollTop; 297 | }; 298 | 299 | p._digest = (function () { 300 | var reBpCond = /((?:>|<)?=?)\s*?((?:-(?!p))|(?:p(?!-)))?(\d+)/; 301 | 302 | var _handleStyle, 303 | 304 | _handleClass, _addClasses, _removeClasses, 305 | _digestClassCounts, _updateClasses, 306 | 307 | _handleBrdcst, _apply; 308 | 309 | /** 310 | * Style related functions 311 | */ 312 | _handleStyle = function (locals, config) { 313 | config.target.css(config.styleGetter(config.scope, locals)); 314 | }; 315 | 316 | /** 317 | * Class related functions 318 | */ 319 | _addClasses = function (config, classes) { 320 | var attr = config.attr; 321 | var newClasses = _digestClassCounts(config, classes, 1); 322 | attr.$addClass(newClasses); 323 | }; 324 | _removeClasses = function (config, classes) { 325 | var attr = config.attr; 326 | var newClasses = _digestClassCounts(config, classes, -1); 327 | attr.$removeClass(newClasses); 328 | }; 329 | _digestClassCounts = function (config, classes, count) { 330 | var element = config.target; 331 | var classCounts = element.data('$classCounts') || {}; 332 | var classesToUpdate = []; 333 | angular.forEach(classes, function (className) { 334 | if (count > 0 || classCounts[className]) { 335 | classCounts[className] = (classCounts[className] || 0) + count; 336 | if (classCounts[className] === +(count > 0)) { 337 | classesToUpdate.push(className); 338 | } 339 | } 340 | }); 341 | element.data('$classCounts', classCounts); 342 | return classesToUpdate.join(' '); 343 | }; 344 | _updateClasses = function (config, oldClasses, newClasses) { 345 | var element = config.target; 346 | var toAdd = arrayDifference(newClasses, oldClasses); 347 | var toRemove = arrayDifference(oldClasses, newClasses); 348 | toRemove = _digestClassCounts(config, toRemove, -1); 349 | toAdd = _digestClassCounts(config, toAdd, 1); 350 | 351 | if (toAdd.length === 0) { 352 | $animate.removeClass(element, toRemove); 353 | } else if (toRemove.length === 0) { 354 | $animate.addClass(element, toAdd); 355 | } else { 356 | $animate.setClass(element, toAdd, toRemove); 357 | } 358 | config.scope.$digest(); 359 | }; 360 | _handleClass = function (locals, config) { 361 | var newVal = config.classGetter(config.scope, locals), 362 | oldVal = config._oldClassVal; 363 | var newClasses = arrayClasses(newVal || []); 364 | if (!oldVal) { 365 | _addClasses(config, newClasses); 366 | } 367 | else if (!angular.equals(newVal, oldVal)) { 368 | var oldClasses = arrayClasses(oldVal); 369 | _updateClasses(config, oldClasses, newClasses); 370 | } 371 | config._oldClassVal = shallowCopy(newVal); 372 | }; 373 | 374 | /** 375 | * Broadcast condition -> event 376 | */ 377 | _apply = function (scope, fn) { 378 | var phase = scope.$root.$$phase; 379 | if(phase == '$apply' || phase == '$digest') { 380 | if(fn && (typeof(fn) === 'function')) { 381 | fn(); 382 | } 383 | } else { 384 | scope.$apply(fn); 385 | } 386 | }; 387 | _handleBrdcst = function (locals, config) { 388 | angular.forEach(config.brdcstList, function (v) { 389 | var active, funcName; 390 | 391 | funcName = config.brdcstIsEmit ? '$emit' : '$broadcast'; 392 | 393 | if (v.always) { 394 | config.brdcstScope[funcName](v.event, null, locals); 395 | } 396 | else if (v.condition) { 397 | active = v.condition(config.scope, locals); 398 | if (v.wasActive === null || active !== v.wasActive) { 399 | _apply(config.brdcstScope, function () { 400 | config.brdcstScope[funcName](v.event, active, locals); 401 | }); 402 | } 403 | v.wasActive = active; 404 | } 405 | }); 406 | }; 407 | 408 | function _update(configId) { 409 | var positive, negative, numReverse, processConfig; 410 | 411 | var containerHeight = this._containerHeight(), 412 | contentHeight = this._contentHeight(), 413 | maxScrollTop = contentHeight - containerHeight, 414 | scrollTop = this._scrollTop(); 415 | 416 | var self = this; 417 | 418 | positive = function (negative) { 419 | return maxScrollTop + negative; 420 | }; 421 | negative = function (positive) { 422 | return positive - maxScrollTop; 423 | }; 424 | numReverse = function (num) { 425 | return (num > 0) ? negative(num) : positive(num); 426 | }; 427 | 428 | processConfig = function (config) { 429 | var from, to, locals, progress, elmMetrics; 430 | 431 | from = config.from < 0 ? positive(config.from) : config.from; 432 | to = config.to < 0 ? positive(config.to) : config.to; 433 | 434 | /** 435 | * Create local context 436 | */ 437 | if (scrollTop < from) { 438 | progress = 0; 439 | locals = { 440 | $positive: from, 441 | $negative: negative(from) 442 | }; 443 | } 444 | else if (scrollTop > to) { 445 | progress = 1; 446 | locals = { 447 | $positive: to, 448 | $negative: negative(to) 449 | }; 450 | } 451 | else if (scrollTop >= from && scrollTop <= to) { 452 | progress = (scrollTop - from) / (to - from); 453 | locals = { 454 | $positive: scrollTop, 455 | $negative: negative(scrollTop) 456 | }; 457 | } 458 | 459 | locals.$progress = progress; 460 | locals.$percentage = progress * 100; 461 | 462 | if (!config._lastProgress || config._lastProgress === progress) { 463 | locals.$direction = 0; 464 | } 465 | else if (config._lastProgress > progress) { 466 | locals.$direction = -1; 467 | } 468 | else if (config._lastProgress < progress) { 469 | locals.$direction = 1; 470 | } 471 | 472 | locals.$height = config.target[0].offsetHeight; 473 | angular.forEach( 474 | self._getElementMetrics(config.target), 475 | function (v, k) { 476 | locals['$' + k] = v; 477 | }); 478 | 479 | /** 480 | * Applying 481 | */ 482 | // to style 483 | if (config.styleGetter) { 484 | _handleStyle(locals, config); 485 | } 486 | 487 | // to class 488 | if (config.classGetter) { 489 | _handleClass(locals, config); 490 | } 491 | 492 | // trigger breakpoints 493 | if (config.brdcstList) { 494 | _handleBrdcst(locals, config); 495 | } 496 | 497 | config._lastProgress = progress; 498 | }; 499 | 500 | if (angular.isUndefined(configId)) { 501 | angular.forEach(this.configs, processConfig); 502 | } 503 | else if (this.configs[configId]) { 504 | processConfig(this.configs[configId]); 505 | } 506 | } 507 | 508 | return function ($event, configId) { 509 | if (this.element === null) { 510 | return; 511 | } 512 | 513 | if (this._digesting && angular.isUndefined(configId)) { 514 | return; 515 | } 516 | 517 | var self = this; 518 | 519 | this._digesting = true; 520 | 521 | requestAnimFrame(function () { 522 | _update.call(self, configId); 523 | 524 | self._digesting = false; 525 | }); 526 | }; 527 | }()); 528 | 529 | p._bind = function ($elm) { 530 | this._binded = true; 531 | 532 | if (this._isDefault()) { 533 | $win 534 | .on('scroll', this.scrollHandler) 535 | .on('resize', this.scrollHandler); 536 | } 537 | else { 538 | $win.on('resize', this.scrollHandler); 539 | $elm.on('scroll', this.scrollHandler); 540 | } 541 | }; 542 | 543 | p._unbind = function ($elm) { 544 | this._binded = false; 545 | 546 | if (this._isDefault()) { 547 | $win 548 | .off('scroll', this.scrollHandler) 549 | .off('resize', this.scrollHandler); 550 | } 551 | else { 552 | $win.off('resize', this.scrollHandler); 553 | $elm.off('scroll', this.scrollHandler); 554 | } 555 | }; 556 | 557 | return function scrollWatchStageFactory(name, $elm) { 558 | return new Stage(name, $elm); 559 | }; 560 | }) 561 | 562 | .service('scrollWatchService', 563 | function scrollWatchService( 564 | scrollWatchStageFactory, $window, $log, $cacheFactory 565 | ) { 566 | var defaultStage = scrollWatchStageFactory( 567 | STAGE_NAME_DEFAULT, 568 | angular.element($window) 569 | ); 570 | 571 | this.stages = $cacheFactory(CACHE_ID_STAGE_POOL); 572 | 573 | this.stages.put(STAGE_NAME_DEFAULT, defaultStage); 574 | 575 | this.addStage = function (stageName, $elm) { 576 | var stage = this.stages.get(stageName); 577 | 578 | if (stage) { 579 | stage.setElement($elm); 580 | return; 581 | } 582 | 583 | stage = scrollWatchStageFactory(stageName, $elm); 584 | this.stages.put(stageName, stage); 585 | }; 586 | 587 | this.removeStage = function (stageName) { 588 | var stage = this.stages.get(stageName); 589 | 590 | if (stage) { 591 | stage.clearElement(); 592 | this._checkStageDestroy(stage); 593 | } 594 | }; 595 | 596 | this.addConfig = function (config) { 597 | var stageName = config.stage, 598 | stage = this.stages.get(stageName); 599 | 600 | if (!stage) { 601 | // Create a stage without element 602 | stage = scrollWatchStageFactory(stageName); 603 | this.stages.put(stageName, stage); 604 | } 605 | 606 | return [stageName, stage.addConfig(config)]; 607 | }; 608 | 609 | this.removeConfig = function (handle) { 610 | var configId = handle[1], 611 | stage = this._getStage(handle); 612 | 613 | if (stage) { 614 | stage.removeConfig(configId); 615 | this._checkStageDestroy(stage); 616 | } 617 | }; 618 | 619 | this.digest = function (handle) { 620 | var configId = handle[1], 621 | stage = this._getStage(handle); 622 | 623 | if (stage) { 624 | stage.digest(configId); 625 | } 626 | }; 627 | 628 | this._checkStageDestroy = function (stage) { 629 | if (stage.couldDestroy()) { 630 | stage.destroy(); 631 | this.stages.remove(stage.name); 632 | } 633 | }; 634 | 635 | this._getStage = function (handle) { 636 | var stageName = handle[0]; 637 | return this.stages.get(stageName); 638 | }; 639 | }) 640 | 641 | .directive('scrollWatch', function (scrollWatchService, $parse) { 642 | return { 643 | restrict: 'A', 644 | link: function postLink(scope, iElm, iAttrs) { 645 | var configHandle, deregisterDigestSync; 646 | 647 | if (iAttrs[DIR_STYLE] || iAttrs[DIR_CLASS] || iAttrs[DIR_BROADCAST]) { 648 | scope.$watch(iAttrs.scrollWatch, scrollWatchChange, true); 649 | 650 | iElm.on('$destroy', function () { 651 | if (configHandle) { 652 | scrollWatchService.removeConfig(configHandle); 653 | } 654 | }); 655 | } 656 | 657 | function scrollWatchChange(config) { 658 | if (config && angular.isObject(config)) { 659 | // Make sure it won't modify the scope variable 660 | config = angular.copy(config); 661 | 662 | if (configHandle) { 663 | scrollWatchService.removeConfig(configHandle); 664 | (deregisterDigestSync || angular.noop)(); 665 | } 666 | 667 | config.target = iElm; 668 | config.scope = scope; 669 | config.stage = config.stage || STAGE_NAME_DEFAULT; 670 | 671 | if (iAttrs[DIR_STYLE]) { 672 | config.styleExpr = iAttrs[DIR_STYLE]; 673 | } 674 | 675 | if (iAttrs[DIR_CLASS]) { 676 | config.classExpr = iAttrs[DIR_CLASS]; 677 | config.attr = iAttrs; 678 | } 679 | 680 | if (iAttrs[DIR_BROADCAST]) { 681 | config.brdcstExpr = iAttrs[DIR_BROADCAST]; 682 | } 683 | 684 | if (config.digestSync) { 685 | deregisterDigestSync = scope.$watch(digestSync); 686 | } 687 | 688 | configHandle = scrollWatchService.addConfig(config); 689 | } 690 | } 691 | 692 | function digestSync () { 693 | if (configHandle) { 694 | scrollWatchService.digest(configHandle); 695 | } 696 | } 697 | } 698 | }; 699 | }) 700 | 701 | .directive('swStage', function (scrollWatchService) { 702 | return { 703 | restrict: 'A', 704 | link: function postLink(scope, iElm, iAttrs) { 705 | var stageName; 706 | 707 | iAttrs.$observe(DIR_STAGE, function (val) { 708 | if (val) { 709 | if (stageName) { 710 | scrollWatchService.removeStage(stageName); 711 | } 712 | 713 | stageName = val; 714 | scrollWatchService.addStage(stageName, iElm); 715 | } 716 | }); 717 | 718 | iElm.on('$destroy', function () { 719 | if (stageName) { 720 | scrollWatchService.removeStage(stageName); 721 | } 722 | }); 723 | } 724 | }; 725 | }); 726 | 727 | function objectSize(obj) { 728 | var c = 0; 729 | angular.forEach(obj, function () { 730 | c++; 731 | }); 732 | return c; 733 | } 734 | 735 | function arrayDifference(tokens1, tokens2) { 736 | var values = []; 737 | 738 | outer: 739 | for(var i = 0; i < tokens1.length; i++) { 740 | var token = tokens1[i]; 741 | for(var j = 0; j < tokens2.length; j++) { 742 | if(token == tokens2[j]) continue outer; 743 | } 744 | values.push(token); 745 | } 746 | return values; 747 | } 748 | 749 | function arrayClasses (classVal) { 750 | if (angular.isArray(classVal)) { 751 | return classVal; 752 | } else if (angular.isString(classVal)) { 753 | return classVal.split(' '); 754 | } else if (angular.isObject(classVal)) { 755 | var classes = [], i = 0; 756 | angular.forEach(classVal, function(v, k) { 757 | if (v) { 758 | classes = classes.concat(k.split(' ')); 759 | } 760 | }); 761 | return classes; 762 | } 763 | return classVal; 764 | } 765 | 766 | function shallowCopy(src, dst) { 767 | if (angular.isArray(src)) { 768 | dst = dst || []; 769 | 770 | for ( var i = 0; i < src.length; i++) { 771 | dst[i] = src[i]; 772 | } 773 | } else if (angular.isObject(src)) { 774 | dst = dst || {}; 775 | 776 | for (var key in src) { 777 | if (hasOwnProperty.call(src, key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) { 778 | dst[key] = src[key]; 779 | } 780 | } 781 | } 782 | 783 | return dst || src; 784 | } 785 | 786 | return MODULE_NAME; 787 | })); 788 | --------------------------------------------------------------------------------