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><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%;
}