diff --git a/README.md b/README.md
index 3350b24..005a71d 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,182 @@
-angular-smooth-scroll
-=====================
+Angular smooth scroll
+==============
-Set of directives and service for animating scrolling to elements.
+A pure-javascript library and set of directives to scroll smoothly to an element with easing. Easing support contributed by Willem Liu with code from Gaëtan Renaudeau.
+
+No jQuery required.
+
+# Features
+
+ * Exposes a service that scrolls the window to an element's location
+ * Provides two directives that enable smooth scrolling to elements.
+ * Clean: No classes are added, no jQuery is required, no CSS files or configuration is needed.
+
+
+# Installation
+
+Include the .js file in your page then enable usage of the directive by including the "smoothScroll" module
+as a dependency
+
+
+# Bower
+
+Install with bower with:
+
+```bash
+bower install ngSmoothScroll
+```
+
+# Usage - As a directive
+
+This module provides two directives:
+
+####smoothScroll:
+
+Attribute. Scrolls the window to this element, optionally validating the expression inside scroll-if.
+
+Example:
+```html
+
+// Basic - The window will scroll to this element's position when compiling this directive
+
+
+
+// With options
+
{{...}}
+
+
+// With condition
+
{{...}}
+
+
+// Inside ng-repeat
+
{{...}}
+```
+
+
+####scrollTo:
+
+Attribute. Scrolls the window to the specified element ID when clicking this element.
+
+Example:
+```html
+
+// Basic
+Click me!
+
+
+// With options
+
+```
+
+
+# Usage - As a service
+
+Inject the 'smoothScroll' service in your directive / factory / controller / whatever, and call like this:
+
+```js
+
+// Using defaults
+var element = document.getElementById('my-elem');
+smoothScroll(element);
+
+
+// With options
+var element = $elem[0];
+
+var options = {
+ duration: 700,
+ easing: 'easeInQuad',
+ offset: 120,
+ callbackBefore: function(element) {
+ console.log('about to scroll to element', element);
+ },
+ callbackAfter: function(element) {
+ console.log('scrolled to element', element);
+ }
+}
+
+smoothScroll(element, options);
+
+
+// In directive's link function
+link: function($scope, $elem, $attrs){
+ var options = $attrs;
+
+ smoothScroll($elem[0], options);
+}
+
+
+```
+
+### Options
+
+#### duration
+Type: `Integer`
+Default: `0`
+
+The duration of the smooth scroll, in miliseconds.
+
+#### offset
+Type: `Integer`
+Default: `0`
+
+The offset from the top of the page in which the scroll should stop.
+
+#### easing
+type: `string`
+default: `easeinoutquart`
+
+the easing function to be used for this scroll.
+
+#### callbackBefore
+type: `function`
+default: `function(element) {}`
+
+a callback function to run before the scroll has started. It is passed the
+element that will be scrolled to.
+
+#### callbackAfter
+type: `function`
+default: `function(element) {}`
+
+a callback function to run after the scroll has completed. It is passed the
+element that was scrolled to.
+
+### Easing functions
+
+The available easing functions are:
+ * 'easeInQuad'
+ * 'easeOutQuad'
+ * 'easeInOutQuad'
+ * 'easeInCubic'
+ * 'easeOutCubic'
+ * 'easeInOutCubic'
+ * 'easeInQuart'
+ * 'easeOutQuart'
+ * 'easeInOutQuart'
+ * 'easeInQuint'
+ * 'easeOutQuint'
+ * 'easeInOutQuint'
+
+#### Credits
+
+Callback hooks contributed by Ben Armston.
+https://github.com/benarmston
+
+Easing support contributed by Willem Liu.
+https://github.com/willemliu
+
+Easing functions forked from Gaëtan Renaudeau.
+https://gist.github.com/gre/1650294
+
+Infinite loop bugs in iOS and Chrome (when zoomed) by Alex Guzman.
+https://github.com/alexguzman
+
+Influenced by Chris Ferdinandi
+https://github.com/cferdinandi
+
+Free to use under the MIT License.
+
+
+Cheers.
diff --git a/angular-smooth-scroll.js b/angular-smooth-scroll.js
new file mode 100644
index 0000000..125da0a
--- /dev/null
+++ b/angular-smooth-scroll.js
@@ -0,0 +1,215 @@
+/* =============================================================
+/*
+/* Angular Smooth Scroll 1.7.0
+/* Animates scrolling to elements, by David Oliveros.
+/*
+/* Callback hooks contributed by Ben Armston
+/* https://github.com/benarmston
+/*
+/* Easing support contributed by Willem Liu.
+/* https://github.com/willemliu
+/*
+/* Easing functions forked from Gaëtan Renaudeau.
+/* https://gist.github.com/gre/1650294
+/*
+/* Infinite loop bugs in iOS and Chrome (when zoomed) by Alex Guzman.
+/* https://github.com/alexguzman
+/*
+/* Influenced by Chris Ferdinandi
+/* https://github.com/cferdinandi
+/*
+/*
+/* Free to use under the MIT License.
+/*
+/* ============================================================= */
+
+angular.module('smoothScroll', [])
+
+
+// Scrolls the window to this element, optionally validating an expression
+//
+.directive('smoothScroll', ['$timeout', 'smoothScroll', function($timeout, smoothScroll){
+ return {
+ restrict: 'A',
+ scope: {
+ callbackBefore: '&',
+ callbackAfter: '&',
+ },
+ link: function($scope, $elem, $attrs){
+ $timeout(function(){
+ if ( typeof $attrs.scrollIf === 'undefined' || $attrs.scrollIf === 'true' ){
+ var callbackBefore = function(element) {
+ if ( $attrs.callbackBefore ) {
+ var exprHandler = $scope.callbackBefore({element: element});
+ if (typeof exprHandler === 'function') {
+ exprHandler(element);
+ }
+ }
+ }
+ var callbackAfter = function(element) {
+ if ( $attrs.callbackAfter ) {
+ var exprHandler = $scope.callbackAfter({element: element});
+ if (typeof exprHandler === 'function') {
+ exprHandler(element);
+ }
+ }
+ }
+ smoothScroll($elem[0], {
+ duration: $attrs.duration,
+ offset: $attrs.offset,
+ easing: $attrs.easing,
+ callbackBefore: callbackBefore,
+ callbackAfter: callbackAfter
+ });
+ }
+
+ });
+ }
+ };
+}])
+
+
+// Scrolls to a specified element ID when this element is clicked
+//
+.directive('scrollTo', ['smoothScroll', function(smoothScroll){
+ return {
+ restrict: 'A',
+ scope: {
+ callbackBefore: '&',
+ callbackAfter: '&',
+ },
+ link: function($scope, $elem, $attrs){
+ var targetElement;
+
+ $elem.on('click', function(e){
+ targetElement = document.getElementById($attrs.scrollTo);
+
+ if ( targetElement ) {
+ e.preventDefault();
+
+ var callbackBefore = function(element) {
+ if ( $attrs.callbackBefore ) {
+ var exprHandler = $scope.callbackBefore({element: element});
+ if (typeof exprHandler === 'function') {
+ exprHandler(element);
+ }
+ }
+ }
+ var callbackAfter = function(element) {
+ if ( $attrs.callbackAfter ) {
+ var exprHandler = $scope.callbackAfter({element: element});
+ if (typeof exprHandler === 'function') {
+ exprHandler(element);
+ }
+ }
+ }
+
+ smoothScroll(targetElement, {
+ duration: $attrs.duration,
+ offset: $attrs.offset,
+ easing: $attrs.easing,
+ callbackBefore: callbackBefore,
+ callbackAfter: callbackAfter
+ });
+
+ return false;
+ }
+ });
+ }
+ };
+}])
+
+
+// Smooth scrolls the window to the provided element.
+//
+.factory('smoothScroll', ['$timeout', function($timeout){
+
+ var getScrollLocation = function() {
+ return window.pageYOffset ? window.pageYOffset : document.documentElement.scrollTop;
+ };
+
+ var smoothScroll = function (element, options) {
+ $timeout(function(){
+ var startLocation = getScrollLocation(),
+ timeLapsed = 0,
+ percentage, position;
+
+
+ // Options
+ //
+ options = options || {};
+ var duration = options.duration || 800,
+ offset = options.offset || 0,
+ easing = options.easing || 'easeInOutQuart',
+ callbackBefore = options.callbackBefore || function() {},
+ callbackAfter = options.callbackAfter || function() {};
+
+
+ // Calculate the easing pattern
+ //
+ var easingPattern = function (type, time) {
+ if ( type == 'easeInQuad' ) return time * time; // accelerating from zero velocity
+ if ( type == 'easeOutQuad' ) return time * (2 - time); // decelerating to zero velocity
+ if ( type == 'easeInOutQuad' ) return time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time; // acceleration until halfway, then deceleration
+ if ( type == 'easeInCubic' ) return time * time * time; // accelerating from zero velocity
+ if ( type == 'easeOutCubic' ) return (--time) * time * time + 1; // decelerating to zero velocity
+ if ( type == 'easeInOutCubic' ) return time < 0.5 ? 4 * time * time * time : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1; // acceleration until halfway, then deceleration
+ if ( type == 'easeInQuart' ) return time * time * time * time; // accelerating from zero velocity
+ if ( type == 'easeOutQuart' ) return 1 - (--time) * time * time * time; // decelerating to zero velocity
+ if ( type == 'easeInOutQuart' ) return time < 0.5 ? 8 * time * time * time * time : 1 - 8 * (--time) * time * time * time; // acceleration until halfway, then deceleration
+ if ( type == 'easeInQuint' ) return time * time * time * time * time; // accelerating from zero velocity
+ if ( type == 'easeOutQuint' ) return 1 + (--time) * time * time * time * time; // decelerating to zero velocity
+ if ( type == 'easeInOutQuint' ) return time < 0.5 ? 16 * time * time * time * time * time : 1 + 16 * (--time) * time * time * time * time; // acceleration until halfway, then deceleration
+ return time; // no easing, no acceleration
+ };
+
+
+ // Calculate how far to scroll
+ //
+ var getEndLocation = function (element) {
+ var location = 0;
+ if (element.offsetParent) {
+ do {
+ location += element.offsetTop;
+ element = element.offsetParent;
+ } while (element);
+ }
+ location = Math.max(location - offset, 0);
+ return location;
+ };
+ var endLocation = getEndLocation(element);
+ var distance = endLocation - startLocation;
+
+
+ // Stop the scrolling animation when the anchor is reached (or at the top/bottom of the page)
+ //
+ var stopAnimation = function () {
+ var currentLocation = getScrollLocation();
+ if ( position == endLocation || currentLocation == endLocation || ( (window.innerHeight + currentLocation) >= document.body.scrollHeight ) ) {
+ clearInterval(runAnimation);
+ callbackAfter(element);
+ }
+ };
+
+
+ // Scroll the page by an increment, and check if it's time to stop
+ //
+ var animateScroll = function () {
+ timeLapsed += 16;
+ percentage = ( timeLapsed / duration );
+ percentage = ( percentage > 1 ) ? 1 : percentage;
+ position = startLocation + ( distance * easingPattern(easing, percentage) );
+ window.scrollTo( 0, position );
+ stopAnimation();
+ };
+
+
+ // Init
+ //
+ callbackBefore(element);
+ var runAnimation = setInterval(animateScroll, 16);
+ });
+ };
+
+ return smoothScroll;
+}]);
diff --git a/angular-smooth-scroll.min.js b/angular-smooth-scroll.min.js
new file mode 100644
index 0000000..13f2ee5
--- /dev/null
+++ b/angular-smooth-scroll.min.js
@@ -0,0 +1 @@
+angular.module("smoothScroll",[]).directive("smoothScroll",["$timeout","smoothScroll",function(e,t){return{restrict:"A",scope:{callbackBefore:"&",callbackAfter:"&"},link:function(n,o,a){e(function(){if(void 0===a.scrollIf||"true"===a.scrollIf){var e=function(e){if(a.callbackBefore){var t=n.callbackBefore({element:e});"function"==typeof t&&t(e)}},c=function(e){if(a.callbackAfter){var t=n.callbackAfter({element:e});"function"==typeof t&&t(e)}};t(o[0],{duration:a.duration,offset:a.offset,easing:a.easing,callbackBefore:e,callbackAfter:c})}})}}}]).directive("scrollTo",["smoothScroll",function(e){return{restrict:"A",scope:{callbackBefore:"&",callbackAfter:"&"},link:function(t,n,o){var a;n.on("click",function(n){if(a=document.getElementById(o.scrollTo)){n.preventDefault();var c=function(e){if(o.callbackBefore){var n=t.callbackBefore({element:e});"function"==typeof n&&n(e)}},r=function(e){if(o.callbackAfter){var n=t.callbackAfter({element:e});"function"==typeof n&&n(e)}};return e(a,{duration:o.duration,offset:o.offset,easing:o.easing,callbackBefore:c,callbackAfter:r}),!1}})}}}]).factory("smoothScroll",["$timeout",function(e){var t=function(){return window.pageYOffset?window.pageYOffset:document.documentElement.scrollTop},n=function(n,o){e(function(){var e,a,c=t(),r=0;o=o||{};var f=o.duration||800,l=o.offset||0,u=o.easing||"easeInOutQuart",i=o.callbackBefore||function(){},s=o.callbackAfter||function(){},d=function(e,t){return"easeInQuad"==e?t*t:"easeOutQuad"==e?t*(2-t):"easeInOutQuad"==e?.5>t?2*t*t:-1+(4-2*t)*t:"easeInCubic"==e?t*t*t:"easeOutCubic"==e?--t*t*t+1:"easeInOutCubic"==e?.5>t?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1:"easeInQuart"==e?t*t*t*t:"easeOutQuart"==e?1- --t*t*t*t:"easeInOutQuart"==e?.5>t?8*t*t*t*t:1-8*--t*t*t*t:"easeInQuint"==e?t*t*t*t*t:"easeOutQuint"==e?1+--t*t*t*t*t:"easeInOutQuint"==e?.5>t?16*t*t*t*t*t:1+16*--t*t*t*t*t:t},b=function(e){var t=0;if(e.offsetParent)do t+=e.offsetTop,e=e.offsetParent;while(e);return t=Math.max(t-l,0)},k=b(n),m=k-c,v=function(){var e=t();a!=k&&e!=k&&window.innerHeight+e1?1:e,a=c+m*d(u,e),window.scrollTo(0,a),v()};i(n);var g=setInterval(I,16)})};return n}]);
\ No newline at end of file
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..ac6daf6
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,26 @@
+{
+ "name": "angular-smooth-scroll",
+ "version": "1.7.0",
+ "homepage": "https://github.com/d-oliveros/angular-smooth-scroll",
+ "authors": [
+ "David Oliveros "
+ ],
+ "description": "A pure-javascript library and set of directives to scroll smoothly to an element with easing.",
+ "main": "angular-smooth-scroll.js",
+ "dependencies": {
+ "angular": "^1.2.0"
+ },
+ "keywords": [
+ "angularjs",
+ "smooth",
+ "scroll"
+ ],
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ]
+}