define(['angular'], function (angular) { var allTips; var tips = []; var tipInterval; var scopeApply; function updateAllTips() { var anyMutation = false; tips.forEach(function (tip) { if (angular.isFunction(tip)) { var muted = tip(); if (muted) anyMutation = true; } }); if (anyMutation) { if (angular.isFunction(scopeApply)) scopeApply(); } } var tipInterval = setInterval(updateAllTips, 500); tipInterval; addEventListener("resize", updateAllTips); return angular.module('cooltip', []) .directive('cooltip', [function () { return { restrict: 'E', require: null, scope: { customIcon: "@", customMaxWidth: "@" }, transclude: true, controller: ["$scope", "$rootScope", "$timeout", function ($scope, $rootScope) { //helper if (!scopeApply && $rootScope) { scopeApply = function () { if (!$rootScope.$$phase) { $rootScope.$apply(); } } } if (!allTips) { allTips = new WeakSet(); } //the default inline styles that go on the controls $scope.containerStyle = {}; $scope.maskStyle = {}; $scope.arrowStyle = {}; $scope.innerContentStyle = {}; $scope.contentsStyle = { position: 'absolute' }; $scope.fillPercentage $scope.initialize = function (cooltip, element, parent, maxWidth) { var hasMaxWidth = typeof maxWidth !== 'undefined' && maxWidth !== null; //this is called once to update the size of the animation box function updateWidthInternal() { var parentRect = parent.getBoundingClientRect(); var cooltipRect = cooltip.getBoundingClientRect(); var elementRect = element.getBoundingClientRect(); var containerRect = document.querySelector('.sidetip-container'); var viewtRect = document.querySelector('.sidetip-view'); //retrieves the values required to perform the calculations for the positioning of the tooltips containerRect = containerRect && containerRect.getBoundingClientRect(); viewtRect = viewtRect && viewtRect.getBoundingClientRect(); var sidetip = angular.element(cooltip); if (containerRect && viewtRect && stretchedOut(parentRect, viewtRect, containerRect) && !sidetip.hasClass('hover-only')) { sidetip.addClass('sidetip'); return applySidetip(sidetip, parentRect, cooltipRect, elementRect, containerRect, viewtRect); } else { sidetip.removeClass('sidetip'); return applyFulltip(parentRect); } } function stretchedOut(parentRect, viewtRect, containerRect) { var parentToBase = parentRect.width + (parentRect.left - viewtRect.left + viewtRect.right - parentRect.right); var fillPercentage = ((parentToBase + ((viewtRect.left - containerRect.left) * 2)) / containerRect.width) * 100; return fillPercentage < 100; } function applyFulltip(parentRect) { $scope.elementWidth = parentRect.width; var ew = $scope.elementWidth; $scope.containerStyle.height = "" + 500 + "px"; if (ew) { $scope.containerStyle.left = 'initial'; $scope.containerStyle.width = "" + ew + "px"; $scope.innerContentStyle.width = (ew - 20) + 'px'; $scope.contentsStyle.width = "" + (ew - 20) + "px"; $scope.contentsStyle.top = ''; } else { delete $scope.containerStyle.width; delete $scope.contentsStyle.width; } return true; } function applySidetip(sidetip, parentRect, cooltipRect, elementRect, containerRect, viewtRect) { delete $scope.containerStyle.width; delete $scope.contentsStyle.width; delete $scope.contentsStyle.top; var coolTipWidth = cooltipRect.width; var translateLeft = 0; //calculates the width of the tooltop using the remaining space between the parent and the container coolTipWidth = (containerRect.right - viewtRect.right - 30); //calculates the translation required to position the tooltip at the end of the container translateLeft = (viewtRect.right - cooltipRect.right) + ((containerRect.width - viewtRect.width) - (coolTipWidth+15)); //sets the width the tooltip needs to display at $scope.containerStyle.width = coolTipWidth + 'px'; $scope.innerContentStyle.width = coolTipWidth + 'px'; $scope.arrowStyle = { display: "block" } $scope.containerStyle.left = translateLeft + 'px'; $scope.containerStyle.position = "absolute"; $scope.contentsStyle.top = (-10) + 'px'; return true; } function updateWidth() { if (updateWidthInternal()) { if (!$rootScope.$$phase) { $scope.$apply(); } } } if (!allTips.has(cooltip)) { allTips.add(cooltip); tips.push(updateWidthInternal); } updateWidth(); cooltip.classList.add("strapped"); if (hasMaxWidth) { $scope.contentsStyle.maxWidth = maxWidth; } } }], template: '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
', link: function (scope, element, attr, ctrls) { //If no icon is specified, deafult the "?" as the cooltip icon if (typeof scope.customIcon === 'undefined' || scope.customIcon === null || scope.customIcon === '') { scope.customIcon = 'icon-hint'; } //find the tip container and this element's parent var c = element[0]; var e = element[0].querySelector(".cooltip-contents > div"); var p = element[0].parentNode; //initialize the tip scope.initialize(c, e, p, scope.customMaxWidth); //when clicked, the item becomes focused (and stays visible). element.on("click", function () { if (angular.isUndefined(element.attr("tabindex"))) { element.attr("tabindex", "0"); window.setTimeout(function () { element.focus(); }, 0); } else { element.blur(); element.removeAttr("tabindex"); } }); //when blurred, will not be able focus again via tabbing into it. element.on("blur", function () { if (!angular.isUndefined(element.attr("tabindex"))) { element.blur(); element.removeAttr("tabindex"); } }); } } }]); });