|
|
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'] ? '<span>' +
|
|
|
'<a class="btn btn-danger" ng-click="on_confirm()">Confirm</a> ' +
|
|
|
'<a class="btn btn-info" ng-click="on_cancel()">Cancel</a>' +
|
|
|
'</span>' : '';
|
|
|
var template =
|
|
|
'<div class="popover fade {{placement}} in" ng-show="popoover_show == \'in\'">' +
|
|
|
'<div class="arrow"></div>' +
|
|
|
'<h3 class="popover-title">{{title}}</h3>' +
|
|
|
'<div class="popover-content" ng-bind-html="content">' +
|
|
|
'</div>' +
|
|
|
'<div class="popover-content" ng-show="show_confirm_template">' +
|
|
|
confirm_template +
|
|
|
'</div>' +
|
|
|
'</div>';
|
|
|
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;
|
|
|
}
|
|
|
};
|
|
|
}]); |