Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

Commit

Permalink
feat(tooltip): added tooltip-html-unsafe directive
Browse files Browse the repository at this point in the history
The directive displays the unsanitized HTML in the tooltip instead of
the escaped text.

The $tooltip service has been modified to allow a little more
flexibility in terms of the prefix used on the $observe'd attributes.
For example, the `tooltip-html-unsafe` directive needs to be called as
written, but it would be nonsensical to require all other attributes
(like animation or placement) to also use that verbose prefix as opposed
to the simpler and more familiar `tooltip-` prefix. The service now
allows independent specification of the name and its prefix.

Lastly, the docs for the tooltip and popover have been updated to show
their available optional attributes.

Closes #246
  • Loading branch information
Josh David Miller committed Apr 3, 2013
1 parent 78da771 commit 45ed280
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 10 deletions.
11 changes: 11 additions & 0 deletions src/popover/docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,14 @@ directive supports multiple placements, optional transition animation, and more.

Like the Twitter Bootstrap jQuery plugin, the popover **requires** the tooltip
module.

The popover directives provides several optional attributes to control how it
will display:

- `popover-title`: A string to display as a fancy title.
- `popover-placement`: Where to place it? Defaults to "top", but also accepts
"bottom", "left", or "right".
- `popover-animation`: Should it fade in and out? Defaults to "true".
- `popover-popup-delay`: For how long should the user have to have the mouse
over the element before the popover shows (in milliseconds)? Defaults to 0.

2 changes: 1 addition & 1 deletion src/popover/popover.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
};
})
.directive( 'popover', [ '$compile', '$timeout', '$parse', '$window', '$tooltip', function ( $compile, $timeout, $parse, $window, $tooltip ) {
return $tooltip( 'popover', 'click' );
return $tooltip( 'popover', 'popover', 'click' );
}]);

4 changes: 4 additions & 0 deletions src/tooltip/docs/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@
at elementum eu, facilisis sed odio morbi quis commodo odio. In cursus
<a><span tooltip-popup-delay='1000' tooltip='appears with delay'>delayed</span></a> turpis massa tincidunt dui ut.
</p>

<p>
I can even contain HTML. <a><span tooltip-html-unsafe="{{htmlTooltip}}">Check me out!</span></a>
</p>
</div>
</div>
1 change: 1 addition & 0 deletions src/tooltip/docs/demo.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var TooltipDemoCtrl = function ($scope) {
$scope.dynamicTooltip = "Hello, World!";
$scope.dynamicTooltipText = "dynamic";
$scope.htmlTooltip = "I've been made <b>bold</b>!";
};
16 changes: 16 additions & 0 deletions src/tooltip/docs/readme.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,18 @@
A lightweight, extensible directive for fancy tooltip creation. The tooltip
directive supports multiple placements, optional transition animation, and more.

There are two versions of the tooltip: `tooltip` and `tooltip-html-unsafe`. The
former takes text only and will escape any HTML provided. The latter takes
whatever HTML is provided and displays it in a tooltip; it called "unsafe"
because the HTML is not sanitized. *The user is responsible for ensuring the
content is safe to put into the DOM!*

The tooltip directives provide several optional attributes to control how they
will display:

- `tooltip-placement`: Where to place it? Defaults to "top", but also accepts
"bottom", "left", or "right".
- `tooltip-animation`: Should it fade in and out? Defaults to "true".
- `tooltip-popup-delay`: For how long should the user have to have the mouse
over the element before the tooltip shows (in milliseconds)? Defaults to 0.

38 changes: 38 additions & 0 deletions src/tooltip/test/tooltip.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,44 @@ describe('tooltip', function() {

});

describe( 'tooltipHtmlUnsafe', function() {
var elm, elmBody, scope;

// load the tooltip code
beforeEach(module('ui.bootstrap.tooltip', function ( $tooltipProvider ) {
$tooltipProvider.options({ animation: false });
}));

// load the template
beforeEach(module('template/tooltip/tooltip-html-unsafe-popup.html'));

beforeEach(inject(function($rootScope, $compile) {
scope = $rootScope;
scope.html = 'I say: <strong class="hello">Hello!</strong>';

elmBody = $compile( angular.element(
'<div><span tooltip-html-unsafe="{{html}}">Selector Text</span></div>'
))( scope );
scope.$digest();
elm = elmBody.find('span');
elmScope = elm.scope();
}));

it( 'should show on mouseenter and hide on mouseleave', inject( function () {
expect( elmScope.tt_isOpen ).toBe( false );

elm.trigger( 'mouseenter' );
expect( elmScope.tt_isOpen ).toBe( true );
expect( elmBody.children().length ).toBe( 2 );

expect( elmScope.tt_content ).toEqual( scope.html );

elm.trigger( 'mouseleave' );
expect( elmScope.tt_isOpen ).toBe( false );
expect( elmBody.children().length ).toBe( 1 );
}));
});

describe( '$tooltipProvider', function() {

describe( 'popupDelay', function() {
Expand Down
45 changes: 36 additions & 9 deletions src/tooltip/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,35 @@ angular.module( 'ui.bootstrap.tooltip', [] )
angular.extend( globalOptions, value );
};

/**
* This is a helper function for translating camel-case to snake-case.
*/
function snake_case(name){
var regexp = /[A-Z]/g;
var separator = '-';
return name.replace(regexp, function(letter, pos) {
return (pos ? separator : '') + letter.toLowerCase();
});
}

/**
* Returns the actual instance of the $tooltip service.
* TODO support multiple triggers
*/
this.$get = [ '$window', '$compile', '$timeout', '$parse', '$document', function ( $window, $compile, $timeout, $parse, $document ) {
return function $tooltip ( type, defaultTriggerShow, defaultTriggerHide ) {
return function $tooltip ( type, prefix, defaultTriggerShow, defaultTriggerHide ) {
var options = angular.extend( {}, defaultOptions, globalOptions );
var directiveName = snake_case( type );

var template =
'<'+ type +'-popup '+
'<'+ directiveName +'-popup '+
'title="{{tt_title}}" '+
'content="{{tt_content}}" '+
'placement="{{tt_placement}}" '+
'animation="tt_animation()" '+
'is-open="tt_isOpen"'+
'>'+
'</'+ type +'-popup>';
'</'+ directiveName +'-popup>';

// Calculate the current position and size of the directive element.
function getPosition( element ) {
Expand All @@ -75,19 +87,19 @@ angular.module( 'ui.bootstrap.tooltip', [] )
scope.tt_content = val;
});

attrs.$observe( type+'Title', function ( val ) {
attrs.$observe( prefix+'Title', function ( val ) {
scope.tt_title = val;
});

attrs.$observe( type+'Placement', function ( val ) {
attrs.$observe( prefix+'Placement', function ( val ) {
scope.tt_placement = angular.isDefined( val ) ? val : options.placement;
});

attrs.$observe( type+'Animation', function ( val ) {
attrs.$observe( prefix+'Animation', function ( val ) {
scope.tt_animation = angular.isDefined( val ) ? $parse( val ) : function(){ return options.animation; };
});

attrs.$observe( type+'PopupDelay', function ( val ) {
attrs.$observe( prefix+'PopupDelay', function ( val ) {
var delay = parseInt( val, 10 );
scope.tt_popupDelay = ! isNaN(delay) ? delay : options.popupDelay;
});
Expand Down Expand Up @@ -232,6 +244,21 @@ angular.module( 'ui.bootstrap.tooltip', [] )
})

.directive( 'tooltip', [ '$tooltip', function ( $tooltip ) {
return $tooltip( 'tooltip', 'mouseenter', 'mouseleave' );
}]);
return $tooltip( 'tooltip', 'tooltip', 'mouseenter', 'mouseleave' );
}])

.directive( 'tooltipHtmlUnsafePopup', function () {
return {
restrict: 'E',
replace: true,
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
};
})

.directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {
return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter', 'mouseleave' );
}])

;

4 changes: 4 additions & 0 deletions template/tooltip/tooltip-html-unsafe-popup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div class="tooltip {{placement}}" ng-class="{ in: isOpen(), fade: animation() }">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner" ng-bind-html-unsafe="content"></div>

This comment has been minimized.

Copy link
@jojosati

jojosati Jul 8, 2013

IMO both templates should merged into one, and use boolean attrs.tooptipHtml like twitter's tooltip.

<div class="tooptip-inner">
   <div ng-bind="!htmlFlag && content"></div>
   <div ng-bind-html-unsafe="htmlFlag || content"></div>
</div>
</div>

0 comments on commit 45ed280

Please sign in to comment.