angular.module('bravoUiPopover', []) .directive("bravoPopover", function($compile, $position, $sce){ // $parse : ng表达式 {{1+2}} {{text}} // $compile : 编译一段html字符串(可以包括ng表达式) return { restrict: "A", scope: { confirm: '&bravoPopoverConfirm' }, compile: function (elem, attr) { var confirm_template = attr['bravoPopoverConfirm'] ? '' + 'Confirm ' + 'Cancel' + '' : ''; var template = '
' + '
' + '

{{title}}

' + '
' + '
' + '
' + confirm_template + '
' + '
'; var linker = $compile(template); return function (scope, elem, attr) { scope.popoover_show = ""; scope.title = attr['title']; scope.content = $sce.trustAsHtml(attr['content']); scope.placement = attr['placement'] ? attr['placement'] : 'top'; scope.trigger = attr['bravoPopoverConfirm'] ? 'click' : attr['trigger']; scope.show_confirm_template = attr['bravoPopoverConfirm'] ? true : false; var tooltip = linker(scope, function (o) { elem.after(o); }); tooltip.css({ top: 0, left: 0, display: 'block' }); if (!scope.trigger || scope.trigger == 'click') { elem.on('click', function (event) { toggle(); }); } else { var eventIn = scope.trigger == 'hover' ? 'mouseenter' : 'focus'; var eventOut = scope.trigger == 'hover' ? 'mouseleave' : 'blur'; elem.on(eventIn, function (event) { show_popover(); }); elem.on(eventOut, function () { hide_popover(); }); } var toggle = function() { scope.popoover_show == 'in'? hide_popover() : show_popover(); render_css(tooltip); }; var show_popover = function() { scope.popoover_show = "in"; scope.$apply(); render_css(tooltip); }; var hide_popover = function() { scope.popoover_show = ""; scope.$apply(); }; var render_css = function (scope_element) { var ttPosition = $position.positionElements(elem, scope_element, scope.placement, false); ttPosition.top += 'px'; ttPosition.left += 'px'; // Now set the calculated positioning. scope_element.css( ttPosition ); }; render_css(tooltip); scope.on_cancel = function () { scope.popoover_show = ""; }; scope.on_confirm = function () { scope.confirm(); scope.popoover_show = ""; }; } } }; }) .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 - offsetParentEl.scrollTop; offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft; } var boundingClientRect = element[0].getBoundingClientRect(); return { width: boundingClientRect.width || element.prop('offsetWidth'), height: boundingClientRect.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: boundingClientRect.width || element.prop('offsetWidth'), height: boundingClientRect.height || element.prop('offsetHeight'), top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop), left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft) }; }, /** * Provides coordinates for the targetEl in relation to hostEl */ positionElements: function (hostEl, targetEl, positionStr, appendToBody) { var positionStrParts = positionStr.split('-'); var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center'; var hostElPos, targetElWidth, targetElHeight, targetElPos; hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl); targetElWidth = targetEl.prop('offsetWidth'); targetElHeight = targetEl.prop('offsetHeight'); var shiftWidth = { center: function () { return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2; }, left: function () { return hostElPos.left; }, right: function () { return hostElPos.left + hostElPos.width; } }; var shiftHeight = { center: function () { return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2; }, top: function () { return hostElPos.top; }, bottom: function () { return hostElPos.top + hostElPos.height; } }; switch (pos0) { case 'right': targetElPos = { top: shiftHeight[pos1](), left: shiftWidth[pos0]() }; break; case 'left': targetElPos = { top: shiftHeight[pos1](), left: hostElPos.left - targetElWidth }; break; case 'bottom': targetElPos = { top: shiftHeight[pos0](), left: shiftWidth[pos1]() }; break; default: targetElPos = { top: hostElPos.top - targetElHeight, left: shiftWidth[pos1]() }; break; } return targetElPos; } }; }]);