<!DOCTYPE html>
<html>

  <head>
    <script data-require="angular.js@1.4.7" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <link href="ng-scrollable.css" rel="stylesheet">
    <script src="ng-scrollable.js"></script>
    <script>var app = angular.module('app', ['ngScrollable']);</script>
  </head>

  <body ng-app="app">
      <div id="someView" ng-scrollable>
        <div id="someContent">
          Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
          Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
          Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
          Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
          Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
        </div>
      </div>

  </body>

</html>
#someView {
  width: 90%; 
  max-height: 500px; /* this works only if I remove max- 
                    but in my specific case I need  max- to make the div collapse if its content is less than 500px high */
  border:1px solid green;
  overflow: hidden;
  margin: 10px;
}

/* =========================================================
 * ng-scrollable.js v0.2.0
 * http://github.com/echa/ng-scrollable
 * =========================================================
 * Copyright 2014-2015 Alexander Eichhorn
 *
 * The MIT License (MIT) Copyright (c) 2014-2015 Alexander Eichhorn.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 * ========================================================= */

angular.module('ngScrollable', [])

.directive('ngScrollable', ['$injector', function ($injector) {
    'use strict';

    // dependencies
    var $document        = $injector.get('$document');
    var $interval        = $injector.get('$interval');
    var $timeout         = $injector.get('$timeout');
    var $window          = $injector.get('$window');
    var $parse           = $injector.get('$parse');
    var extend           = angular.extend;
    var element          = angular.element;
    var isDefined        = angular.isDefined;
    var isTouchDevice    = typeof $window.ontouchstart !== 'undefined';
    var xform            = 'transform';

    // use requestAnimationFrame for kinetic scrolling
    var $$rAF = $window.requestAnimationFrame || $window.webkitRequestAnimationFrame;

    // use MutationObserver to auto-refresh on DOM changes
    // https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;

    // Angular used to contain an internal service that is using a task queue
    // in 1.4.x which makes it incompatible with smooth scrolling
    //
    // var $$rAF            = $injector.get('$$rAF');

    // find the correct CSS transform feature class name
    ['webkit', 'moz', 'o', 'ms'].every(function (prefix) {
      var e = prefix + 'Transform';
      var body = $document.find('body').eq(0);
      if (typeof body[0].style[e] !== 'undefined') {
        xform = e;
        return false;
      }
      return true;
    });

    var defaultOpts = {
      id: 0,
      scrollX: 'bottom',
      scrollY: 'right',
      scrollXSlackSpace: 0,
      scrollYSlackSpace: 0,
      scrollXAlways: false,
      scrollYAlways: false,
      usePadding: false,
      useObserver: true,
      wheelSpeed: 1,
      minSliderLength: 10,
      useBothWheelAxes: false,
      useKeyboard: true,
      updateOnResize: true,
      kineticTau: 325
    };

    return {
      restrict: 'A',
      transclude: true,
      template: "<div class=\"scrollable\"><div class=\"scrollable-content\" ng-transclude></div><div class='scrollable-bar scrollable-bar-x'><div class='scrollable-slider'></div></div><div class='scrollable-bar scrollable-bar-y'><div class='scrollable-slider'></div></div></div>",
      link: function ($scope, elem, attrs) {
        var
        config = extend({}, defaultOpts, $scope.$eval(attrs.ngScrollable)),
        el = element(elem.children()[0]),
        dom = {
          window: element($window),
          el: el,
          content: element(el.children()[0]),
          barX: element(el.children()[1]),
          barY: element(el.children()[2]),
          sliderX: element(element(el.children()[1]).children()[0]),
          sliderY: element(element(el.children()[2]).children()[0])
        },
        domObserver,
        isXActive = false,
        isYActive = false,
        containerWidth = 0,
        containerHeight = 0,
        contentWidth = 0,
        contentHeight = 0,
        contentTop = 0,
        contentLeft = 0,
        xSliderWidth = 0,
        xSliderLeft = 0,
        ySliderHeight = 0,
        ySliderTop = 0,
        dragStartLeft = null,
        dragStartPageX = null,
        dragStartTop = null,
        dragStartPageY = null,
        isXScrolling = false,
        isYScrolling = false,
        hovered = false,
        activeTimeout,
        spySetter = {},
        // kinetic scrolling
        velocityX = 0,
        amplitudeX = 0,
        frameX = 0,
        targetX = 0,
        velocityY = 0,
        amplitudeY = 0,
        frameY = 0,
        targetY = 0,
        trackTime,
        trackerTimeout,

        toPix = function (v) { return v.toFixed(3) + 'px'; },
        clamp = function (val, min, max) {
          return Math.max(min, Math.min(val, max));
        },
        updateSliderX = function () {
          // adjust container width by the amount of border pixels so that the
          // slider does not extend outside the bar region
          var cw = containerWidth - 3;
          if (isXActive) {
            xSliderWidth = Math.max(config.minSliderLength, parseInt(cw * cw / contentWidth, 10));
            xSliderLeft = parseInt(contentLeft * (cw - xSliderWidth) / (contentWidth - cw), 10);

            if (xSliderLeft >= cw - xSliderWidth) {
              xSliderLeft = cw - xSliderWidth;
            } else if (xSliderLeft < 0) {
              xSliderLeft = 0;
            }
            dom.sliderX[0].style[xform] = 'translate3d(' + toPix(xSliderLeft) + ',0,0)';
            dom.sliderX[0].style.width = toPix(xSliderWidth);
          } else {
            xSliderWidth = xSliderLeft = 0;
            dom.sliderX[0].style[xform] = 'translate3d(0,0,0)';
            dom.sliderX[0].style.width = '0';
          }
        },
        updateSliderY = function () {
          // adjust container height by the amount of border pixels so that the
          // slider does not extend outside the bar region
          var ch = containerHeight - 3;
          if (isYActive) {
            ySliderHeight = Math.max(config.minSliderLength, parseInt(ch * ch / contentHeight, 10));
            ySliderTop = parseInt(contentTop * (ch - ySliderHeight) / (contentHeight - ch), 10);

            if (ySliderTop >= ch - ySliderHeight) {
              ySliderTop = ch - ySliderHeight;
            } else if (ySliderTop < 0) {
              ySliderTop = 0;
            }
            dom.sliderY[0].style[xform] = 'translate3d(0,' + toPix(ySliderTop) + ',0)';
            dom.sliderY[0].style.height = toPix(ySliderHeight);
          } else {
            ySliderTop = ySliderHeight = 0;
            dom.sliderY[0].style[xform] = 'translate3d(0,0,0)';
            dom.sliderY[0].style.height = '0';
          }
        },
        updateBarX = function () {
          var showAlways = config.scrollXAlways,
              scrollbarXStyles = {left: 0, width: toPix(containerWidth), display: isXActive || showAlways ? "inherit" : "none"};
          switch (config.scrollX) {
          case 'bottom':
            scrollbarXStyles.bottom = 0;
            dom.content[isXActive || showAlways ? 'addClass' : 'removeClass']('scrollable-bottom');
            dom.barX[isXActive || showAlways ? 'addClass' : 'removeClass']('scrollable-bottom');
            break;
          case 'top':
            scrollbarXStyles.top = 0;
            dom.content[isXActive || showAlways ? 'addClass' : 'removeClass']('scrollable-top');
            dom.barX[isXActive || showAlways ? 'addClass' : 'removeClass']('scrollable-top');
            break;
          }
          dom.barX.css(scrollbarXStyles);
          dom.sliderX[0].style.display = isXActive ? 'inherit' : 'none';
        },
        updateBarY = function () {
          var showAlways = config.scrollYAlways,
              scrollbarYStyles = {top: 0, height: toPix(containerHeight), display: isYActive || showAlways ? "inherit" : "none"};
          switch (config.scrollY) {
          case 'right':
            scrollbarYStyles.right = 0;
            dom.content[isYActive || showAlways ? 'addClass' : 'removeClass']('scrollable-right');
            dom.barY[isYActive || showAlways ? 'addClass' : 'removeClass']('scrollable-right');
            break;
          case 'left':
            scrollbarYStyles.left = 0;
            dom.content[isYActive || showAlways ? 'addClass' : 'removeClass']('scrollable-left');
            dom.barY[isYActive || showAlways ? 'addClass' : 'removeClass']('scrollable-left');
            break;
          }
          dom.barY.css(scrollbarYStyles);
          dom.sliderY[0].style.display = isYActive ? 'inherit' : 'none';
        },
        scrollTo = function (left, top) {
          // clamp to 0 .. content{Height|Width} - container{Height|Width}
          contentTop = clamp(top, 0, contentHeight - containerHeight);
          contentLeft = clamp(left, 0, contentWidth - containerWidth);
          dom.content[0].style[xform] = 'translate3d(' + toPix(-contentLeft) + ',' + toPix(-contentTop) + ',0)';
          // update external scroll spies
          if (spySetter.spyX) {
            spySetter.spyX($scope, parseInt(contentLeft, 10));
          }
          if (spySetter.spyY) {
            spySetter.spyY($scope, parseInt(contentTop, 10));
          }
        },
        scrollX = function (pos) {
          if (!isXActive) { return; }
          scrollTo(pos, contentTop);
          updateSliderX();
        },
        scrollY = function (pos) {
          if (!isYActive) { return; }
          scrollTo(contentLeft, pos);
          updateSliderY();
        },
        refresh = function (event, noNotify) {
          // read DOM
          containerWidth = config.usePadding ? dom.el[0].clientWidth : dom.el[0].offsetWidth; // innerWidth() : elm[0].width();
          containerHeight = config.usePadding ? dom.el[0].clientHeight : dom.el[0].offsetHeight; // elm[0].innerHeight() : elm[0].height();
          contentWidth = dom.content[0].scrollWidth;
          contentHeight = dom.content[0].scrollHeight;

          // activate scrollbars
          if (config.scrollX !== 'none' && containerWidth + config.scrollXSlackSpace < contentWidth) {
            isXActive = true;
          }
          else {
            scrollX(0);
            isXActive = false;
          }

          if (config.scrollY !== 'none' && containerHeight + config.scrollYSlackSpace < contentHeight) {
            isYActive = true;
          }
          else {
            scrollY(0);
            isYActive = false;
          }

          // update UI
          updateBarX();
          updateBarY();
          updateSliderX();
          updateSliderY();

          // make sure scroll position isn't beyond content bounds
          if (contentWidth < contentLeft + xSliderLeft + xSliderWidth) {
            scrollX(xSliderLeft);
          }
          if (contentHeight < contentTop + ySliderTop + ySliderHeight) {
            scrollY(ySliderTop);
          }

          // broadcast the new dimensions down the scope stack so inner content
          // controllers can react appropriately
          if (!noNotify) {
            $scope.$broadcast('scrollable.dimensions', containerWidth, containerHeight, contentWidth, contentHeight, config.id);
          }
        },
        stop = function (e, prevent) {
          e.stopPropagation();
          if (prevent) { e.preventDefault(); }
          return false;
        },
        ypos = function (e) {
          e = e.originalEvent || e;
          // touch event
          if (e.targetTouches && (e.targetTouches.length >= 1)) {
            return e.targetTouches[0].pageY;
          }
          // mouse event
          return e.pageY;
        },
        xpos = function (e) {
          e = e.originalEvent || e;
          // touch event
          if (e.targetTouches && (e.targetTouches.length >= 1)) {
            return e.targetTouches[0].pageX;
          }
          // mouse event
          return e.pageX;
        },
        track = function () {
          var now, elapsed, delta, v;

          now = Date.now();
          elapsed = now - trackTime;
          trackTime = now;

          // X
          delta = contentLeft - frameX;
          frameX = contentLeft;
          v = 1000 * delta / (1 + elapsed);
          velocityX = 0.8 * v + 0.2 * velocityX;
          // Y
          delta = contentTop - frameY;
          frameY = contentTop;
          v = 1000 * delta / (1 + elapsed);
          velocityY = 0.8 * v + 0.2 * velocityY;

        },
        autoScrollX = function () {
          var elapsed, delta;
          if (amplitudeX) {
            elapsed = Date.now() - trackTime;
            delta = -amplitudeX * Math.exp(-elapsed / config.kineticTau);
            if (delta > 0.5 || delta < -0.5) {
              scrollX(targetX + delta);
              $$rAF(autoScrollX);
            } else {
              scrollX(targetX);
            }
          }
        },
        autoScrollY = function () {
          var elapsed, delta;
          if (amplitudeY) {
            elapsed = Date.now() - trackTime;
            delta = -amplitudeY * Math.exp(-elapsed / config.kineticTau);
            if (delta > 0.5 || delta < -0.5) {
              scrollY(targetY + delta);
              $$rAF(autoScrollY);
            } else {
              scrollY(targetY);
            }
          }
        },
        onMouseDownX = function (e) {
          dragStartPageX = xpos(e);
          dragStartLeft = contentLeft;
          isXScrolling = true;
          velocityX = amplitudeX = 0;
          frameX = contentLeft;
          if (!trackerTimeout) { trackerTimeout = $interval(track, 100); }
          dom.el.addClass('active');
          return isTouchDevice || stop(e, !isTouchDevice);
        },
        onMouseMoveX = function (e) {
          if (isXScrolling) {
            // scale slider move to content width
            var deltaSlider = xpos(e) - dragStartPageX,
                deltaContent = isTouchDevice ? -deltaSlider : parseInt(deltaSlider * (contentWidth - containerWidth) / (containerWidth - xSliderWidth), 10);
            scrollX(dragStartLeft + deltaContent);
            return stop(e, true);
          }
        },
        onMouseUpX = function (e) {
          if (isXScrolling) {
            isXScrolling = false;
            dom.el.removeClass('active');
            dragStartLeft = dragStartPageX = null;
          }
          // kinetic scroll
          if (trackerTimeout) { $interval.cancel(trackerTimeout); trackerTimeout = null; }
          if (velocityX > 10 || velocityX < -10) {
            amplitudeX = 0.8 * velocityX;
            targetX = Math.round(contentLeft + amplitudeX);
            trackTime = Date.now();
            $$rAF(autoScrollX);
          }
          return isTouchDevice || stop(e, !isTouchDevice);
        },
        onMouseDownY = function (e) {
          dragStartPageY = ypos(e);
          dragStartTop = contentTop;
          isYScrolling = true;
          velocityY = amplitudeY = 0;
          frameY = contentTop;
          if (!trackerTimeout) { trackerTimeout = $interval(track, 100); }
          dom.el.addClass('active');
          return isTouchDevice || stop(e, !isTouchDevice);
        },
        onMouseMoveY =  function (e) {
          if (isYScrolling) {
            var deltaSlider = ypos(e) - dragStartPageY,
                deltaContent = isTouchDevice ? -deltaSlider : parseInt(deltaSlider * (contentHeight - containerHeight) / (containerHeight - ySliderHeight), 10);
            scrollY(dragStartTop + deltaContent);
            return stop(e, true);
          }
        },
        onMouseUpY =  function (e) {
          if (isYScrolling) {
            isYScrolling = false;
            dom.el.removeClass('active');
            dragStartTop = dragStartPageY = null;
          }
          // kinetic scroll
          if (trackerTimeout) { $interval.cancel(trackerTimeout); trackerTimeout = null; }
          if (velocityY > 10 || velocityY < -10) {
            amplitudeY = 0.8 * velocityY;
            targetY = Math.round(contentTop + amplitudeY);
            trackTime = Date.now();
            $$rAF(autoScrollY);
          }
          return isTouchDevice || stop(e, true);
        },
        // Get Offset without jquery
        // element.prop('offsetTop')
        // element[0].getBoundingClientRect().top
        clickBarX = function (e) {
          var halfOfScrollbarLength = parseInt(xSliderWidth / 2, 10),
              positionLeft = e.clientX - dom.barX[0].getBoundingClientRect().left - halfOfScrollbarLength,
              maxPositionLeft = containerWidth - xSliderWidth,
              positionRatio = clamp(positionLeft / maxPositionLeft, 0, 1);
          scrollX((contentWidth - containerWidth) * positionRatio);
          $scope.$digest();
        },
        clickBarY = function (e) {
          var halfOfScrollbarLength = parseInt(ySliderHeight / 2, 10),
              positionTop = e.clientY - dom.barY[0].getBoundingClientRect().top - halfOfScrollbarLength,
              maxPositionTop = containerHeight - ySliderHeight,
              positionRatio = clamp(positionTop / maxPositionTop, 0, 1);
          scrollY((contentHeight - containerHeight) * positionRatio);
          $scope.$digest();
        },
        hoverOn = function () { hovered = true; },
        hoverOff = function () { hovered = false; },
        handleKey = function (e) {
          var deltaX = 0, deltaY = 0, s = 30;
          if (!hovered || $document[0].activeElement.isContentEditable ||
            e.altKey || e.ctrlKey || e.metaKey) {
            return;
          }

          switch (e.which) {
          case 37: // left
            deltaX = -s;
            break;
          case 38: // up
            deltaY = s;
            break;
          case 39: // right
            deltaX = s;
            break;
          case 40: // down
            deltaY = -s;
            break;
          case 33: // page up
            deltaY = containerHeight;
            break;
          case 32: // space bar
          case 34: // page down
            deltaY = -containerHeight;
            break;
          case 35: // end
            if (isYActive && !isXActive) {
              deltaY = -contentHeight;
            } else {
              deltaX = containerHeight;
            }
            break;
          case 36: // home
            if (isYActive && !isXActive) {
              deltaY = contentHeight;
            } else {
              deltaX = -containerHeight;
            }
            break;
          default:
            return;
          }

          scrollY(contentTop - deltaY);
          scrollX(contentLeft + deltaX);

          // prevent default scrolling
          e.preventDefault();
          $scope.$digest();
        },
        handleWheel = function (e) {
          // with jquery use e.originalEvent.deltaX!!!
          e = e.originalEvent || e;
          var deltaX = e.deltaX * config.wheelSpeed,
              deltaY = e.deltaY * config.wheelSpeed;

          // avoid flickering in Chrome: disabled animated translate
          dom.el.addClass('active');
          $timeout.cancel(activeTimeout);
          activeTimeout = $timeout(function () {dom.el.removeClass('active'); }, 500);

          if (!config.useBothWheelAxes) {
            // deltaX will only be used for horizontal scrolling and deltaY will
            // only be used for vertical scrolling - this is the default
            scrollY(contentTop + deltaY);
            scrollX(contentLeft + deltaX);
          } else if (isYActive && !isXActive) {
            // only vertical scrollbar is active and useBothWheelAxes option is
            // active, so let's scroll vertical bar using both mouse wheel axes
            if (deltaY) {
              scrollY(contentTop + deltaY);
            } else {
              scrollY(contentTop + deltaX);
            }
          } else if (isXActive && !isYActive) {
            // useBothWheelAxes and only horizontal bar is active, so use both
            // wheel axes for horizontal bar
            if (deltaX) {
              scrollX(contentLeft + deltaX);
            } else {
              scrollX(contentLeft + deltaY);
            }
          }

          // prevent default scrolling
          stop(e, true);
          $scope.$digest();
        },

        registerHandlers = function () {

          // use MutationObserver
          if (config.useObserver && MutationObserver) {

            var observerConfig = {
              // attributes: true, // Note: inefficient on large DOM + Angular!!
              childList: true,
              // characterData: true,
              subtree: true
            };

            // create observer and debounce calls
            domObserver = new MutationObserver(function () {
              $$rAF(refresh);
            });

            // bind observer to scrollable content
            domObserver.observe(dom.content[0], observerConfig);
          }

          // bind DOM element handlers
          if (config.updateOnResize) { dom.window.on('resize', refresh); }

          if (config.scrollX !== 'none') {

            // scrollbar clicks
            dom.sliderX.on('click', stop);
            dom.barX.on('click',    clickBarX);

            if (isTouchDevice) {

              // content touch/drag
              dom.el.on('touchstart', onMouseDownX);
              dom.el.on('touchmove',  onMouseMoveX);
              dom.el.on('touchend',   onMouseUpX);

            } else {

              // slider drag
              dom.sliderX.on('mousedown', onMouseDownX);
              $document.on('mousemove', onMouseMoveX);
              $document.on('mouseup',   onMouseUpX);

            }
          }

          if (config.scrollY !== 'none') {

            // scrollbar clicks
            dom.sliderY.on('click', stop);
            dom.barY.on('click',    clickBarY);


            if (isTouchDevice) {

              // content touch/drag
              dom.el.on('touchstart', onMouseDownY);
              dom.el.on('touchmove',  onMouseMoveY);
              dom.el.on('touchend',   onMouseUpY);

            } else {

              // slider drag
              dom.sliderY.on('mousedown', onMouseDownY);
              $document.on('mousemove',   onMouseMoveY);
              $document.on('mouseup',     onMouseUpY);

            }
          }

          // mouse wheel
          if (!isTouchDevice) {
            dom.el.on('wheel',      handleWheel);
          }

          // keyboard
          if (config.useKeyboard && !isTouchDevice) {
            dom.el.on('mouseenter', hoverOn);
            dom.el.on('mouseleave', hoverOff);
            $document.on('keydown', handleKey);
          }

        },

        unregisterHandlers = function () {

          // disconnect observer
          if (domObserver) {
            domObserver.disconnect();
            domObserver = null;
          }

          // global resize event
          if (config.updateOnResize) { dom.window.off('resize', refresh); }

          // slider events
          dom.sliderX.off('click', stop);
          dom.barX.off('click',    clickBarX);
          dom.sliderY.off('click', stop);
          dom.barY.off('click',    clickBarY);

          // touch
          if (isTouchDevice) {
            dom.el.off('touchstart', onMouseDownX);
            dom.el.off('touchmove',  onMouseMoveX);
            dom.el.off('touchend',   onMouseUpX);
            dom.el.off('touchstart', onMouseDownY);
            dom.el.off('touchmove',  onMouseMoveY);
            dom.el.off('touchend',   onMouseUpY);
          } else {

            // slider drag
            dom.sliderX.off('mousedown', onMouseDownX);
            $document.off('mousemove',   onMouseMoveX);
            $document.off('mouseup',     onMouseUpX);

            dom.sliderY.off('mousedown', onMouseDownY);
            $document.off('mousemove',   onMouseMoveY);
            $document.off('mouseup',     onMouseUpY);

            // keyboard
            if (config.useKeyboard) {
              // mouse hovering activates keyboard capture
              dom.el.off('mouseenter', hoverOn);
              dom.el.off('mouseleave', hoverOff);
              $document.off('keydown', handleKey);
            }

            // mouse wheel
            dom.el.off('wheel',      handleWheel);
          }

        };


        $scope.$on('content.reload', function (e, noNotify) {

          // try unregistering event handlers
          unregisterHandlers();

          // defer to next digest
          $timeout(function () {

            // update DOM node reference (because ui-view replaces nodes)
            dom.el = element(elem.children()[0]);
            dom.content = element(dom.el.children()[0]);

            // register handlers
            registerHandlers();

            // refresh scrollbars
            refresh(e, noNotify);

          });

        });

        // sent by controllers of transcluded content on change
        $scope.$on('content.changed', function (e, wait, noNotify) {

          // ms to wait before refresh
          wait = wait || 100;

          // defer to next digest
          $timeout(function () {

            // refresh scrollbars
            refresh(e, noNotify);

          }, wait);

          e.preventDefault();
        });


        // may be broadcast from outside to scroll to content edges
        $scope.$on('scrollable.scroll.left', function () {
          // defer to next digest
          $scope.$applyAsync(function () { scrollX(0); });
        });

        $scope.$on('scrollable.scroll.right', function () {
          // defer to next digest
          $scope.$applyAsync(function () { scrollX(contentWidth); });
        });

        $scope.$on('scrollable.scroll.top', function () {
          // defer to next digest
          $scope.$applyAsync(function () { scrollY(0); });
        });

        $scope.$on('scrollable.scroll.bottom', function () {
          // defer to next digest
          $scope.$applyAsync(function () { scrollY(contentHeight); });
        });

        // (un)register event handlers on scope destroy
        $scope.$on('$destroy', function () {
          $timeout.cancel(activeTimeout);
          unregisterHandlers();
        });

        // init
        registerHandlers();
        refresh();

        // watch and set spy attribute value expressions
        angular.forEach(['spyX', 'spyY'], function (attr) {
          if (attrs[attr]) {
            // keep a setter to the spy expression (if settable)
            spySetter[attr] = $parse(attrs[attr]).assign;
            // watch the spy expression
            $scope.$watch(attrs[attr], function (val) {
              switch (attr) {
              case 'spyX' :
                scrollX(val);
                break;
              case 'spyY' :
                scrollY(val);
                break;
              }
            });
          }
        });
      }
    };
  }
]);
.scrollable {
    overflow: hidden;
    position: relative;
    width: 100%;
    height: 100%;
}

.scrollable-content {
    box-sizing: border-box;
    min-height: 100%;
}

/* Enable the Classes below to have scrollbars not overlap the content */
/*
.scrollable-content.scrollable-right {
    margin-right: 15px;
}

.scrollable-content.scrollable-top {
    margin-top: 15px;
}

.scrollable-content.scrollable-left {
    margin-left: 15px;
}

.scrollable-content.scrollable-bottom {
    margin-bottom: 15px;
}
*/

/* if any element is fixed and displayed at full window size, this rule becomes handy */
.fullwindow .scrollable-content {
    transform: none !important;
}

.scrollable .scrollable-content,
.scrollable .scrollable-slider {
    -webkit-transition: background-color .2s linear, opacity .2s linear, -webkit-transform .2s ease-out;
       -moz-transition: background-color .2s linear, opacity .2s linear, -moz-transform .2s ease-out;
        -ms-transition: background-color .2s linear, opacity .2s linear, -ms-transform .2s ease-out;
         -o-transition: background-color .2s linear, opacity .2s linear, -o-transform .2s ease-out;
            transition: background-color .2s linear, opacity .2s linear, transform .2s ease-out;
}

.scrollable.active .scrollable-content,
.scrollable.active .scrollable-slider {
    -webkit-transition: none;
       -moz-transition: none;
        -ms-transition: none;
         -o-transition: none;
            transition: none;
}

.scrollable .scrollable-bar {
    position: absolute;
    box-sizing: border-box;
    -webkit-border-radius: 2px;
       -moz-border-radius: 2px;
            border-radius: 2px;
    opacity: 0;
    background-color: #f0f0f0;
    background-color: rgba(255,255,255,0.8);
    border: 2px solid transparent;
}

.scrollable:hover .scrollable-bar {
    opacity: 0.6;
}

.scrollable .scrollable-bar:hover {
    opacity: 1;
}

.scrollable.active .scrollable-bar {
    opacity: 1;
}

.scrollable .scrollable-slider {
    -webkit-border-radius: 2px;
       -moz-border-radius: 2px;
            border-radius: 2px;
    background-color: #ccc;
    background-image: linear-gradient(to right, #ccc, #ddd);
    border: 1px solid #333;
    border: 1px solid rgba(0, 0, 0, 0.5);
}

.scrollable .scrollable-bar-x .scrollable-slider {
    height: 8px;
}

.scrollable .scrollable-bar-y .scrollable-slider {
    width: 8px;
}