var app = angular.module('plunker', []);

app.controller('zln', function($scope){
  $scope.active = 1;
  
  angular.noop();
  });

app.directive('zlnHelp', ['$timeout', '$position', function ($timeout, $position) {
    return {
        restrict: 'A',
        replace: false,
        scope: true,
        transclude: false,
        controller: ['$scope', '$element', '$attrs', '$transclude', function($scope, $element, $attrs, $transclude) {
            var helpers = [];

            this.addHelper = function (helper) {
                helpers.push(helper);
            };

            
            this.show = function () {
                if ($scope.overlay) return false;
                $scope.showOverlay();

                for (var i = 0, len = helpers.length; i < len; i++) {
                    $scope.showElt(helpers[i]);
                };
            };

            $scope.stop = function () {
                if (!$scope.overlay) return false;
                $scope.overlay.remove();
                for (var i = 0, len = helpers.length; i < len; i++) {
                    var elt = helpers[i];
                    elt.helper.remove();
                    elt.tooltip.remove();
                    elt.element.removeClass('chardinjs-show-element');
                    elt.element.removeClass('chardinjs-relative-position');
                };
            }

            $scope.showOverlay = function () {
                var overlay = $scope.overlay = angular.element('<div></div>');
                overlay[0].className = 'chardinjs-overlay';
                if ($element.prop('tagName').toLowerCase() === 'body') {
                    overlay[0].setAttribute('style', 'top: 0;bottom: 0; left: 0;right: 0;position: fixed;');
                } else {
                    var position = $position.offset($element);
                    overlay[0].setAttribute('style', "width: " + element_position.width + "px; height:" + element_position.height + "px; top:" + element_position.top + "px;left: " + element_position.left + "px;");
                }
                $element.append(overlay);
                overlay.bind('click', $scope.stop);
                $timeout(function () { overlay[0].setAttribute('style', (overlay.attr('style') + 'opacity:0.8;')); }, 10)
            };

            $scope.showElt = function (elt) {
                var elt_position = elt.offset, position='';
                var elt_helper = elt.helper = angular.element('<div></div>');
                var elt_tooltip = elt.tooltip = angular.element('<div></div>');
                elt_helper[0].className = "chardinjs-helper-layer chardinjs-" + elt.zlnPosition;
                elt_helper[0].setAttribute("style", "width: " + elt_position.width + "px; height:" + elt_position.height + "px; top:" + elt_position.top + "px; left: " + elt_position.left + "px;");
                $element.append(elt_helper);
                elt_tooltip[0].className = "chardinjs-tooltip chardinjs-" + elt.zlnPosition;
                elt_tooltip[0].innerHTML = "<div class='chardinjs-tooltiptext'>" + elt.zlnIntro + "</div>";
                elt_helper.append(elt_tooltip);
                var hlp_position = $position.offset(elt_tooltip);
                elt_tooltip[0].style.top = null;
                elt_tooltip[0].style.right = null;
                elt_tooltip[0].style.bottom = null;
                elt_tooltip[0].style.left = null;
                switch (elt.zlnPosition) {
                    case 'top':
                    case 'bottom':
                        elt_tooltip[0].style.left = '' + ((elt_position.width / 2) - (hlp_position.width / 2)) + 'px';
                        break;
                    case 'left':
                    case 'right':
                        elt_tooltip[0].style.top = '' + ((elt_position.height / 2) - (hlp_position.height / 2)) + 'px';
                        break;
                };
                switch (elt.zlnPosition) {
                    case 'left':
                        elt_tooltip[0].style.left = '-' + (hlp_position.width - 34) + 'px';
                        break;
                    case 'right':
                        elt_tooltip[0].style.right = '-' + (hlp_position.width - 34) + 'px';
                        break;
                    case 'bottom':
                        elt_tooltip[0].style.bottom = '-' + hlp_position.height + 'px';
                        break;
                    case 'top':
                        elt_tooltip[0].style.top = '-' + hlp_position.height + 'px';
                        break;
                };
                elt.element[0].className += ' chardinjs-show-element';
                if (elt.element[0].currentStyle) {
                    position = elt.element[0].currentStyle["position"];
                } else if (document.defaultView && document.defaultView.getComputedStyle) {
                    position = document.defaultView.getComputedStyle(elt.element[0], null).getPropertyValue("position");
                }
                position = position.toLowerCase();
                if (position !== "absolute" && position !== "relative") {
                    elt.element[0].className += " chardinjs-relative-position";
                }

            }
        }]
    };
}]);

app.directive('zlnHelpTrigger', function () {
    return {
        restrict: 'A',
        replace: false,
        transclude: false,
        require: '^zlnHelp',
        link: function (scope, element, attrs, zlnHelpCtrl) {
            element.bind('click', zlnHelpCtrl.show);
        }
    }
});

app.directive('zlnHelper', ['$position', function ($position) {
    return {
        restrict: 'A',
        replace: false,
        transclude: false,
        require: '^zlnHelp',
        scope: {zlnIntro:'@', zlnPosition:'@'},
        link: function (scope, element, attrs, zlnHelpCtrl) {
            scope.offset = $position.offset(element);
            scope.element = element;
            zlnHelpCtrl.addHelper(scope);

        }
    };
}]);



app.factory('$position', ['$document', '$window', function ($document, $window) {

    function getStyle(el, cssprop) {
        if (el.currentStyle) { //IE
            return el.currentStyle[cssprop];
        } else if ($window.getComputedStyle) {
            return $window.getComputedStyle(el)[cssprop];
        }
        // finally try and get inline style
        return el.style[cssprop];
    }

    /**
     * Checks if a given element is statically positioned
     * @param element - raw DOM element
     */
    function isStaticPositioned(element) {
        return (getStyle(element, "position") || 'static' ) === 'static';
    }

    /**
     * returns the closest, non-statically positioned parentOffset of a given element
     * @param element
     */
    var parentOffsetEl = function (element) {
        var docDomEl = $document[0];
        var offsetParent = element.offsetParent || docDomEl;
        while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
            offsetParent = offsetParent.offsetParent;
        }
        return offsetParent || docDomEl;
    };

    return {
        /**
         * Provides read-only equivalent of jQuery's position function:
         * http://api.jquery.com/position/
         */
        position: function (element) {
            var elBCR = this.offset(element);
            var offsetParentBCR = { top: 0, left: 0 };
            var offsetParentEl = parentOffsetEl(element[0]);
            if (offsetParentEl != $document[0]) {
                offsetParentBCR = this.offset(angular.element(offsetParentEl));
                offsetParentBCR.top += offsetParentEl.clientTop;
                offsetParentBCR.left += offsetParentEl.clientLeft;
            }

            return {
                width: element.prop('offsetWidth'),
                height: element.prop('offsetHeight'),
                top: elBCR.top - offsetParentBCR.top,
                left: elBCR.left - offsetParentBCR.left
            };
        },

        /**
         * Provides read-only equivalent of jQuery's offset function:
         * http://api.jquery.com/offset/
         */
        offset: function (element) {
            var boundingClientRect = element[0].getBoundingClientRect();
            return {
                width: element.prop('offsetWidth'),
                height: element.prop('offsetHeight'),
                top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop),
                left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft)
            };
        }
    };
}]);


<!DOCTYPE html>
<html ng-app="plunker" ng-controller='zln'>

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.0.x" src="http://code.angularjs.org/1.0.7/angular.min.js" data-semver="1.0.7"></script>
    <script src="app.js"></script>
  </head>

  <body zln-help>
    <div class="container">
      <div class="jumbotron">
        <h1 zln-helper zln-intro="Project title" zln-position="right">Chardin.js</h1>
        <p class="lead">
          Simple overlay instructions for your apps.
        </p>
        <a href="#" class="btn btn-large primary" zln-help-trigger data-toggle="chardinjs" zln-intro="This button toggles the overlay, you can click it, even when the overlay is visible" zln-position="left">See it in action</a>
        <a href="" id="opentour" class="hide" zln-help-trigger data-toggle="chardinjs">Or open it again</a>
        <div class="credits">
          <p zln-helper zln-intro="Author of this plugin, aka Pablo Fernandez" zln-position="right">
            Baked with
            <b>&lt;3</b>
            by
            <a href="https://github.com/heelhook">@heelhook</a>.
          </p>
        </div>
      </div>


        <div class="links">
          <p id="links" zln-helper zln-intro="Links to Github, Twitter, etc." zln-position="top">
            <a href="https://twitter.com/share" class="twitter-share-button" data-lang="en" data-text="Check out Chardin.js, simple overlay instructions for apps, https://heelhook.github.com/chardin.js" data-count="none" data-via="pablo2dot0">Tweet</a>
            <a href="https://twitter.com/pablo2dot0" class="twitter-follow-button" data-show-count="false" data-lang="en">Follow @pablo2dot0</a>
            <iframe style="display: inline-block;" frameborder="no" scrolling="no" height="30px" width="70px" src="http://hnlike.com/upvote.php?link=http%3A%2F%2Fheelhook.github.com&title=Chardin">iframes not supported by your browser</iframe>
          </p>
        </div>

      <div class="license">
        Code licensed under the
        <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License v2.0</a>.
      </div>
    </div>
  </body>

</html>


.chardinjs-overlay {
  position: absolute;
  z-index: 999999;
  background-color: #000;
  opacity: 0;
  -webkit-transition: all 0.3s ease-out;
  -moz-transition: all 0.3s ease-out;
  -ms-transition: all 0.3s ease-out;
  -o-transition: all 0.3s ease-out;
  transition: all 0.3s ease-out; }

.chardinjs-helper-layer {
  position: absolute;
  z-index: 9999998;
  color: white;
  -webkit-transition: all 0.3s ease-out;
  -moz-transition: all 0.3s ease-out;
  -ms-transition: all 0.3s ease-out;
  -o-transition: all 0.3s ease-out;
  transition: all 0.3s ease-out; }
  .chardinjs-helper-layer.chardinjs-left {
    border-left: solid white 1px;
    margin-left: -10px; }
  .chardinjs-helper-layer.chardinjs-right {
    border-right: solid white 1px;
    padding-right: 10px; }
  .chardinjs-helper-layer.chardinjs-bottom {
    border-bottom: solid white 1px;
    padding-bottom: 10px; }
  .chardinjs-helper-layer.chardinjs-top {
    border-top: solid white 1px;
    padding-top: 10px; }

.chardinjs-tooltip {
  position: absolute;
  -webkit-transition: opacity 0.1s ease-out;
  -moz-transition: opacity 0.1s ease-out;
  -ms-transition: opacity 0.1s ease-out;
  -o-transition: opacity 0.1s ease-out;
  transition: opacity 0.1s ease-out;
  max-width: 200px; }
  .chardinjs-tooltip.chardinjs-left {
    margin-left: -135px;
    padding-right: 10px; }
  .chardinjs-tooltip.chardinjs-right {
    margin-right: -135px;
    padding-left: 10px; }
  .chardinjs-tooltip.chardinjs-bottom {
    margin-bottom: -50px;
    padding-top: 10px; }
  .chardinjs-tooltip.chardinjs-top {
    margin-top: -50px;
    padding-bottom: 10px; }
  .chardinjs-tooltip.chardinjs-right:before, .chardinjs-tooltip.chardinjs-left:after, .chardinjs-tooltip.chardinjs-bottom:before, .chardinjs-tooltip.chardinjs-top:after {
    content: ".";
    display: inline-block;
    background-color: white;
    height: 1px;
    overflow: hidden;
    position: absolute; }
  .chardinjs-tooltip.chardinjs-right:before, .chardinjs-tooltip.chardinjs-left:after {
    width: 100px;
    top: 50%; }
  .chardinjs-tooltip.chardinjs-bottom:before, .chardinjs-tooltip.chardinjs-top:after {
    width: 1px;
    height: 50px;
    left: 50%; }
  .chardinjs-tooltip.chardinjs-bottom:before {
    top: -50px; }
  .chardinjs-tooltip.chardinjs-top:after {
    bottom: -50px; }
  .chardinjs-tooltip.chardinjs-right:before {
    left: -100px; }
  .chardinjs-tooltip.chardinjs-left:after {
    right: -100px; }

.chardinjs-show-element {
  z-index: 9999999;
  opacity: 0.8; }

.chardinjs-relative-position {
  position: relative; }
  
  body {
  background-color: #fff;
  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#eee), color-stop(25%, #fff), to(#fff));
  background-image: -webkit-linear-gradient(#eee, #fff 25%, #fff);
  background-image: -moz-linear-gradient(top, #eee, #fff 25%, #fff);
  background-image: -ms-linear-gradient(#eee, #fff 25%, #fff);
  background-image: -o-linear-gradient(#eee, #fff 25%, #fff);
  background-image: linear-gradient(#eee, #fff 25%, #fff);
  background-repeat: no-repeat;
  background-attachment: fixed;
}

.primary.btn {
  color: #fff;
  text-decoration: none;
  text-shadow: 0 -1px 0 rgba(0,0,0,.5);
  background-color: #3072b3; /* Old browsers */
  background-repeat: repeat-x; /* Repeat the gradient */
  background-image: -moz-linear-gradient(top, #599bdc 0%, #3072b3 100%); /* FF3.6+ */
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#599bdc), color-stop(100%,#3072b3)); /* Chrome,Safari4+ */
  background-image: -webkit-linear-gradient(top, #599bdc 0%,#3072b3 100%); /* Chrome 10+,Safari 5.1+ */
  background-image: -ms-linear-gradient(top, #599bdc 0%,#3072b3 100%); /* IE10+ */
  background-image: -o-linear-gradient(top, #599bdc 0%,#3072b3 100%); /* Opera 11.10+ */
  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#599bdc', endColorstr='#3072b3',GradientType=0 ); /* IE6-9 */
  background-image: linear-gradient(top, #599bdc 0%,#3072b3 100%); /* W3C */
  border: 1px solid #2967a4;
  -webkit-transition: none;
     -moz-transition: none;
          transition: none;
  -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 2px rgba(0,0,0,.2);
     -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 2px rgba(0,0,0,.2);
          box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 2px rgba(0,0,0,.2);
}
.primary.btn:hover {
  background-position: 0 -15px;
}
.primary.btn:active {
  background-image: none;
  background-color: #3072b3; /* Old browsers */
  -webkit-box-shadow: inset 0 5px 10px rgba(0,0,0,.125), 0 1px 2px rgba(0,0,0,.2);
     -moz-box-shadow: inset 0 5px 10px rgba(0,0,0,.125), 0 1px 2px rgba(0,0,0,.2);
          box-shadow: inset 0 5px 10px rgba(0,0,0,.125), 0 1px 2px rgba(0,0,0,.2);
}

.jumbotron {
  margin: 50px 0;
  text-align: center;
}
.jumbotron h1 {
  font-size: 100px;
  font-family: Montserrat, sans-serif;
  line-height: 1;
  display: inline-block;
  color: #444444;
}

.jumbotron h1.chardinjs-show-element, .chardinjs-show-element {
  color: #cccccc;
}

.chardinjs-show-element a {
  color: rgba(77, 186, 240, 1);
}

.jumbotron .lead {
  font-size: 24px;
  line-height: 1.25;
}

.jumbotron img {
  display: block;
  margin-left: auto;
  margin-right: auto;
  max-height: 300px;
  height: 0px;
}

.jumbotron .btn {
  font-size: 21px;
  padding: 14px 24px;
  margin: 20px;
  margin-bottom: 10px;
}

.links {
  padding-top: 20px;
  text-align: center;
}

.links p, .credits p { display: inline-block; }

.credits, .license {
  text-align: center;
  margin: 10px;
}

.chardinjs-helper-layer[data-id="links"] .chardinjs-tooltip:after {
  left: 10%;
}