Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
feat(ngdocs): support popover, foldouts and foldover annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
matsko authored and mhevery committed Jun 18, 2013
1 parent 07ef166 commit ef22968
Show file tree
Hide file tree
Showing 13 changed files with 636 additions and 6 deletions.
186 changes: 186 additions & 0 deletions docs/component-spec/annotationsSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
describe('Docs Annotations', function() {

beforeEach(module('docsApp'));

var body;
beforeEach(function() {
body = angular.element(document.body);
body.html('');
});

describe('popover directive', function() {

var $scope, element;
beforeEach(inject(function($rootScope, $compile) {
$scope = $rootScope.$new();
element = angular.element(
'<div style="margin:200px;" data-title="title_text" data-content="content_text" popover></div>'
);
element.attr('id','idx');
body.append(element);
$compile(element)($scope);
$scope.$apply();
}));

it('should be hidden by default', inject(function(popoverElement) {
expect(popoverElement.visible()).toBe(false);
}));

it('should capture the click event and set the title and content and position the tip', inject(function(popoverElement) {
element.triggerHandler('click');
expect(popoverElement.isSituatedAt(element)).toBe(true);
expect(popoverElement.visible()).toBe(true);
expect(popoverElement.title()).toBe('title_text');
expect(popoverElement.content()).toContain('content_text');
expect(popoverElement.besideElement.attr('id')).toBe('idx');
}));

it('should hide and clear the title and content if the same element is clicked again', inject(function(popoverElement) {
//show the element
element.triggerHandler('click');
expect(popoverElement.isSituatedAt(element)).toBe(true);

//hide the element
element.triggerHandler('click');
expect(popoverElement.isSituatedAt(element)).toBe(false);
expect(popoverElement.visible()).toBe(false);
expect(popoverElement.title()).toBe('');
expect(popoverElement.content()).toBe('');
}));

it('should parse markdown content', inject(function(popoverElement, $compile) {
element = angular.element(
'<div style="margin:200px;" data-title="#title_text" data-content="#heading" popover></div>'
);
body.append(element);
$compile(element)($scope);
$scope.$apply();
element.triggerHandler('click');
expect(popoverElement.title()).toBe('#title_text');
expect(popoverElement.content()).toBe('<h1 id="heading">heading</h1>');
}));

});


describe('foldout directive', function() {

var $scope, parent, element, url, window;
beforeEach(function() {
module(function($provide, $animationProvider) {
$provide.value('$window', window = angular.mock.createMockWindow());
$animationProvider.register('foldout-enter', function($window) {
return {
start : function(element, done) {
$window.setTimeout(done, 1000);
}
}
});
$animationProvider.register('foldout-hide', function($window) {
return {
start : function(element, done) {
$window.setTimeout(done, 500);
}
}
});
$animationProvider.register('foldout-show', function($window) {
return {
start : function(element, done) {
$window.setTimeout(done, 200);
}
}
});
});
inject(function($rootScope, $compile, $templateCache) {
url = '/page.html';
$scope = $rootScope.$new();
parent = angular.element('<div class="parent"></div>');
element = angular.element('<div data-url="' + url + '" foldout></div>');
body.append(parent);
parent.append(element);
$compile(parent)($scope);
$scope.$apply();
});
});

it('should inform that it is loading', inject(function($httpBackend) {
$httpBackend.expect('GET', url).respond('hello');
element.triggerHandler('click');

var kids = body.children();
var foldout = angular.element(kids[kids.length-1]);
expect(foldout.html()).toContain('loading');
}));

it('should download a foldout HTML page and animate the contents', inject(function($httpBackend) {
$httpBackend.expect('GET', url).respond('hello');

element.triggerHandler('click');
$httpBackend.flush();

window.setTimeout.expect(1).process();
window.setTimeout.expect(1000).process();

var kids = body.children();
var foldout = angular.element(kids[kids.length-1]);
expect(foldout.text()).toContain('hello');
}));

it('should hide then show when clicked again', inject(function($httpBackend) {
$httpBackend.expect('GET', url).respond('hello');

//enter
element.triggerHandler('click');
$httpBackend.flush();
window.setTimeout.expect(1).process();
window.setTimeout.expect(1000).process();

//hide
element.triggerHandler('click');
window.setTimeout.expect(1).process();
window.setTimeout.expect(500).process();

//show
element.triggerHandler('click');
window.setTimeout.expect(1).process();
window.setTimeout.expect(200).process();
}));

});

describe('DocsController fold', function() {

var window, $scope, ctrl;
beforeEach(function() {
module(function($provide, $animationProvider) {
$provide.value('$window', window = angular.mock.createMockWindow());
});
inject(function($rootScope, $controller, $location, $cookies, sections) {
$scope = $rootScope.$new();
ctrl = $controller('DocsController',{
$scope : $scope,
$location : $location,
$window : window,
$cookies : $cookies,
sections : sections
});
});
});

it('should download and reveal the foldover container', inject(function($compile, $httpBackend) {
var url = '/page.html';
var fullUrl = '/notes/' + url;
$httpBackend.expect('GET', fullUrl).respond('hello');

var element = angular.element('<div ng-include="docs_fold"></div>');
$compile(element)($scope);
$scope.$apply();

$scope.fold(url);

$httpBackend.flush();
}));

});

});
7 changes: 5 additions & 2 deletions docs/components/angular-bootstrap/bootstrap-prettify.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,12 @@ directive.code = function() {
directive.prettyprint = ['reindentCode', function(reindentCode) {
return {
restrict: 'C',
terminal: true,
compile: function(element) {
element.html(window.prettyPrintOne(reindentCode(element.html()), undefined, true));
var html = element.html();
//ensure that angular won't compile {{ curly }} values
html = html.replace(/\{\{/g, '<span>{{</span>')
.replace(/\}\}/g, '<span>}}</span>');
element.html(window.prettyPrintOne(reindentCode(html), undefined, true));
}
};
}];
Expand Down
173 changes: 172 additions & 1 deletion docs/components/angular-bootstrap/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,133 @@ directive.table = function() {
};
};

var popoverElement = function() {
var object = {
init : function() {
this.element = angular.element(
'<div class="popover popover-incode top">' +
'<div class="arrow"></div>' +
'<div class="popover-inner">' +
'<div class="popover-title"><code></code></div>' +
'<div class="popover-content"></div>' +
'</div>' +
'</div>'
);
this.node = this.element[0];
this.element.css({
'display':'block',
'position':'absolute'
});
angular.element(document.body).append(this.element);

var inner = this.element.children()[1];
this.titleElement = angular.element(inner.childNodes[0].firstChild);
this.contentElement = angular.element(inner.childNodes[1]);

//stop the click on the tooltip
this.element.bind('click', function(event) {
event.preventDefault();
event.stopPropagation();
});

var self = this;
angular.element(document.body).bind('click',function(event) {
if(self.visible()) self.hide();
});
},

show : function(x,y) {
this.element.addClass('visible');
this.position(x || 0, y || 0);
},

hide : function() {
this.element.removeClass('visible');
this.position(-9999,-9999);
},

visible : function() {
return this.position().y >= 0;
},

isSituatedAt : function(element) {
return this.besideElement ? element[0] == this.besideElement[0] : false;
},

title : function(value) {
return this.titleElement.html(value);
},

content : function(value) {
if(value && value.length > 0) {
value = new Showdown.converter().makeHtml(value);
}
return this.contentElement.html(value);
},

positionArrow : function(position) {
this.node.className = 'popover ' + position;
},

positionAway : function() {
this.besideElement = null;
this.hide();
},

positionBeside : function(element) {
this.besideElement = element;

var elm = element[0];
var x = elm.offsetLeft;
var y = elm.offsetTop;
x -= 30;
y -= this.node.offsetHeight + 10;
this.show(x,y);
},

position : function(x,y) {
if(x != null && y != null) {
this.element.css('left',x + 'px');
this.element.css('top', y + 'px');
}
else {
return {
x : this.node.offsetLeft,
y : this.node.offsetTop
};
}
}
};

object.init();
object.hide();

return object;
};

directive.popover = ['popoverElement', function(popover) {
return {
restrict: 'A',
priority : 500,
link: function(scope, element, attrs) {
element.bind('click',function(event) {
event.preventDefault();
event.stopPropagation();
if(popover.isSituatedAt(element) && popover.visible()) {
popover.title('');
popover.content('');
popover.positionAway();
}
else {
popover.title(attrs.title);
popover.content(attrs.content);
popover.positionBeside(element);
}
});
}
}
}];

directive.tabPane = function() {
return {
require: '^tabbable',
Expand All @@ -208,5 +335,49 @@ directive.tabPane = function() {
};
};

directive.foldout = ['$http', '$animator','$window', function($http, $animator, $window) {
return {
restrict: 'A',
priority : 500,
link: function(scope, element, attrs) {
var animator = $animator(scope, { ngAnimate: "'foldout'" });
var container, loading, url = attrs.url;
if(/\/build\//.test($window.location.href)) {
url = '/build/docs' + url;
}
element.bind('click',function() {
scope.$apply(function() {
if(!container) {
if(loading) return;

loading = true;
var par = element.parent();
container = angular.element('<div class="foldout">loading...</div>');
animator.enter(container, null, par);

$http.get(url, { cache : true }).success(function(html) {
loading = false;

html = '<div class="foldout-inner">' +
'<div calss="foldout-arrow"></div>' +
html +
'</div>';
container.html(html);

//avoid showing the element if the user has already closed it
if(container.css('display') == 'block') {
container.css('display','none');
animator.show(container);
}
});
}
else {
container.css('display') == 'none' ? animator.show(container) : animator.hide(container);
}
});
});
}
}
}];

angular.module('bootstrap', []).directive(directive);
angular.module('bootstrap', []).directive(directive).factory('popoverElement', popoverElement);
Empty file added docs/content/notes/empty.tmp
Empty file.
Loading

0 comments on commit ef22968

Please sign in to comment.