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

Commit ef22968

Browse files
matskomhevery
authored andcommitted
feat(ngdocs): support popover, foldouts and foldover annotations
1 parent 07ef166 commit ef22968

File tree

13 files changed

+636
-6
lines changed

13 files changed

+636
-6
lines changed
+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
describe('Docs Annotations', function() {
2+
3+
beforeEach(module('docsApp'));
4+
5+
var body;
6+
beforeEach(function() {
7+
body = angular.element(document.body);
8+
body.html('');
9+
});
10+
11+
describe('popover directive', function() {
12+
13+
var $scope, element;
14+
beforeEach(inject(function($rootScope, $compile) {
15+
$scope = $rootScope.$new();
16+
element = angular.element(
17+
'<div style="margin:200px;" data-title="title_text" data-content="content_text" popover></div>'
18+
);
19+
element.attr('id','idx');
20+
body.append(element);
21+
$compile(element)($scope);
22+
$scope.$apply();
23+
}));
24+
25+
it('should be hidden by default', inject(function(popoverElement) {
26+
expect(popoverElement.visible()).toBe(false);
27+
}));
28+
29+
it('should capture the click event and set the title and content and position the tip', inject(function(popoverElement) {
30+
element.triggerHandler('click');
31+
expect(popoverElement.isSituatedAt(element)).toBe(true);
32+
expect(popoverElement.visible()).toBe(true);
33+
expect(popoverElement.title()).toBe('title_text');
34+
expect(popoverElement.content()).toContain('content_text');
35+
expect(popoverElement.besideElement.attr('id')).toBe('idx');
36+
}));
37+
38+
it('should hide and clear the title and content if the same element is clicked again', inject(function(popoverElement) {
39+
//show the element
40+
element.triggerHandler('click');
41+
expect(popoverElement.isSituatedAt(element)).toBe(true);
42+
43+
//hide the element
44+
element.triggerHandler('click');
45+
expect(popoverElement.isSituatedAt(element)).toBe(false);
46+
expect(popoverElement.visible()).toBe(false);
47+
expect(popoverElement.title()).toBe('');
48+
expect(popoverElement.content()).toBe('');
49+
}));
50+
51+
it('should parse markdown content', inject(function(popoverElement, $compile) {
52+
element = angular.element(
53+
'<div style="margin:200px;" data-title="#title_text" data-content="#heading" popover></div>'
54+
);
55+
body.append(element);
56+
$compile(element)($scope);
57+
$scope.$apply();
58+
element.triggerHandler('click');
59+
expect(popoverElement.title()).toBe('#title_text');
60+
expect(popoverElement.content()).toBe('<h1 id="heading">heading</h1>');
61+
}));
62+
63+
});
64+
65+
66+
describe('foldout directive', function() {
67+
68+
var $scope, parent, element, url, window;
69+
beforeEach(function() {
70+
module(function($provide, $animationProvider) {
71+
$provide.value('$window', window = angular.mock.createMockWindow());
72+
$animationProvider.register('foldout-enter', function($window) {
73+
return {
74+
start : function(element, done) {
75+
$window.setTimeout(done, 1000);
76+
}
77+
}
78+
});
79+
$animationProvider.register('foldout-hide', function($window) {
80+
return {
81+
start : function(element, done) {
82+
$window.setTimeout(done, 500);
83+
}
84+
}
85+
});
86+
$animationProvider.register('foldout-show', function($window) {
87+
return {
88+
start : function(element, done) {
89+
$window.setTimeout(done, 200);
90+
}
91+
}
92+
});
93+
});
94+
inject(function($rootScope, $compile, $templateCache) {
95+
url = '/page.html';
96+
$scope = $rootScope.$new();
97+
parent = angular.element('<div class="parent"></div>');
98+
element = angular.element('<div data-url="' + url + '" foldout></div>');
99+
body.append(parent);
100+
parent.append(element);
101+
$compile(parent)($scope);
102+
$scope.$apply();
103+
});
104+
});
105+
106+
it('should inform that it is loading', inject(function($httpBackend) {
107+
$httpBackend.expect('GET', url).respond('hello');
108+
element.triggerHandler('click');
109+
110+
var kids = body.children();
111+
var foldout = angular.element(kids[kids.length-1]);
112+
expect(foldout.html()).toContain('loading');
113+
}));
114+
115+
it('should download a foldout HTML page and animate the contents', inject(function($httpBackend) {
116+
$httpBackend.expect('GET', url).respond('hello');
117+
118+
element.triggerHandler('click');
119+
$httpBackend.flush();
120+
121+
window.setTimeout.expect(1).process();
122+
window.setTimeout.expect(1000).process();
123+
124+
var kids = body.children();
125+
var foldout = angular.element(kids[kids.length-1]);
126+
expect(foldout.text()).toContain('hello');
127+
}));
128+
129+
it('should hide then show when clicked again', inject(function($httpBackend) {
130+
$httpBackend.expect('GET', url).respond('hello');
131+
132+
//enter
133+
element.triggerHandler('click');
134+
$httpBackend.flush();
135+
window.setTimeout.expect(1).process();
136+
window.setTimeout.expect(1000).process();
137+
138+
//hide
139+
element.triggerHandler('click');
140+
window.setTimeout.expect(1).process();
141+
window.setTimeout.expect(500).process();
142+
143+
//show
144+
element.triggerHandler('click');
145+
window.setTimeout.expect(1).process();
146+
window.setTimeout.expect(200).process();
147+
}));
148+
149+
});
150+
151+
describe('DocsController fold', function() {
152+
153+
var window, $scope, ctrl;
154+
beforeEach(function() {
155+
module(function($provide, $animationProvider) {
156+
$provide.value('$window', window = angular.mock.createMockWindow());
157+
});
158+
inject(function($rootScope, $controller, $location, $cookies, sections) {
159+
$scope = $rootScope.$new();
160+
ctrl = $controller('DocsController',{
161+
$scope : $scope,
162+
$location : $location,
163+
$window : window,
164+
$cookies : $cookies,
165+
sections : sections
166+
});
167+
});
168+
});
169+
170+
it('should download and reveal the foldover container', inject(function($compile, $httpBackend) {
171+
var url = '/page.html';
172+
var fullUrl = '/notes/' + url;
173+
$httpBackend.expect('GET', fullUrl).respond('hello');
174+
175+
var element = angular.element('<div ng-include="docs_fold"></div>');
176+
$compile(element)($scope);
177+
$scope.$apply();
178+
179+
$scope.fold(url);
180+
181+
$httpBackend.flush();
182+
}));
183+
184+
});
185+
186+
});

docs/components/angular-bootstrap/bootstrap-prettify.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,12 @@ directive.code = function() {
9696
directive.prettyprint = ['reindentCode', function(reindentCode) {
9797
return {
9898
restrict: 'C',
99-
terminal: true,
10099
compile: function(element) {
101-
element.html(window.prettyPrintOne(reindentCode(element.html()), undefined, true));
100+
var html = element.html();
101+
//ensure that angular won't compile {{ curly }} values
102+
html = html.replace(/\{\{/g, '<span>{{</span>')
103+
.replace(/\}\}/g, '<span>}}</span>');
104+
element.html(window.prettyPrintOne(reindentCode(html), undefined, true));
102105
}
103106
};
104107
}];

docs/components/angular-bootstrap/bootstrap.js

+172-1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,133 @@ directive.table = function() {
198198
};
199199
};
200200

201+
var popoverElement = function() {
202+
var object = {
203+
init : function() {
204+
this.element = angular.element(
205+
'<div class="popover popover-incode top">' +
206+
'<div class="arrow"></div>' +
207+
'<div class="popover-inner">' +
208+
'<div class="popover-title"><code></code></div>' +
209+
'<div class="popover-content"></div>' +
210+
'</div>' +
211+
'</div>'
212+
);
213+
this.node = this.element[0];
214+
this.element.css({
215+
'display':'block',
216+
'position':'absolute'
217+
});
218+
angular.element(document.body).append(this.element);
219+
220+
var inner = this.element.children()[1];
221+
this.titleElement = angular.element(inner.childNodes[0].firstChild);
222+
this.contentElement = angular.element(inner.childNodes[1]);
223+
224+
//stop the click on the tooltip
225+
this.element.bind('click', function(event) {
226+
event.preventDefault();
227+
event.stopPropagation();
228+
});
229+
230+
var self = this;
231+
angular.element(document.body).bind('click',function(event) {
232+
if(self.visible()) self.hide();
233+
});
234+
},
235+
236+
show : function(x,y) {
237+
this.element.addClass('visible');
238+
this.position(x || 0, y || 0);
239+
},
240+
241+
hide : function() {
242+
this.element.removeClass('visible');
243+
this.position(-9999,-9999);
244+
},
245+
246+
visible : function() {
247+
return this.position().y >= 0;
248+
},
249+
250+
isSituatedAt : function(element) {
251+
return this.besideElement ? element[0] == this.besideElement[0] : false;
252+
},
253+
254+
title : function(value) {
255+
return this.titleElement.html(value);
256+
},
257+
258+
content : function(value) {
259+
if(value && value.length > 0) {
260+
value = new Showdown.converter().makeHtml(value);
261+
}
262+
return this.contentElement.html(value);
263+
},
264+
265+
positionArrow : function(position) {
266+
this.node.className = 'popover ' + position;
267+
},
268+
269+
positionAway : function() {
270+
this.besideElement = null;
271+
this.hide();
272+
},
273+
274+
positionBeside : function(element) {
275+
this.besideElement = element;
276+
277+
var elm = element[0];
278+
var x = elm.offsetLeft;
279+
var y = elm.offsetTop;
280+
x -= 30;
281+
y -= this.node.offsetHeight + 10;
282+
this.show(x,y);
283+
},
284+
285+
position : function(x,y) {
286+
if(x != null && y != null) {
287+
this.element.css('left',x + 'px');
288+
this.element.css('top', y + 'px');
289+
}
290+
else {
291+
return {
292+
x : this.node.offsetLeft,
293+
y : this.node.offsetTop
294+
};
295+
}
296+
}
297+
};
298+
299+
object.init();
300+
object.hide();
301+
302+
return object;
303+
};
304+
305+
directive.popover = ['popoverElement', function(popover) {
306+
return {
307+
restrict: 'A',
308+
priority : 500,
309+
link: function(scope, element, attrs) {
310+
element.bind('click',function(event) {
311+
event.preventDefault();
312+
event.stopPropagation();
313+
if(popover.isSituatedAt(element) && popover.visible()) {
314+
popover.title('');
315+
popover.content('');
316+
popover.positionAway();
317+
}
318+
else {
319+
popover.title(attrs.title);
320+
popover.content(attrs.content);
321+
popover.positionBeside(element);
322+
}
323+
});
324+
}
325+
}
326+
}];
327+
201328
directive.tabPane = function() {
202329
return {
203330
require: '^tabbable',
@@ -208,5 +335,49 @@ directive.tabPane = function() {
208335
};
209336
};
210337

338+
directive.foldout = ['$http', '$animator','$window', function($http, $animator, $window) {
339+
return {
340+
restrict: 'A',
341+
priority : 500,
342+
link: function(scope, element, attrs) {
343+
var animator = $animator(scope, { ngAnimate: "'foldout'" });
344+
var container, loading, url = attrs.url;
345+
if(/\/build\//.test($window.location.href)) {
346+
url = '/build/docs' + url;
347+
}
348+
element.bind('click',function() {
349+
scope.$apply(function() {
350+
if(!container) {
351+
if(loading) return;
352+
353+
loading = true;
354+
var par = element.parent();
355+
container = angular.element('<div class="foldout">loading...</div>');
356+
animator.enter(container, null, par);
357+
358+
$http.get(url, { cache : true }).success(function(html) {
359+
loading = false;
360+
361+
html = '<div class="foldout-inner">' +
362+
'<div calss="foldout-arrow"></div>' +
363+
html +
364+
'</div>';
365+
container.html(html);
366+
367+
//avoid showing the element if the user has already closed it
368+
if(container.css('display') == 'block') {
369+
container.css('display','none');
370+
animator.show(container);
371+
}
372+
});
373+
}
374+
else {
375+
container.css('display') == 'none' ? animator.show(container) : animator.hide(container);
376+
}
377+
});
378+
});
379+
}
380+
}
381+
}];
211382

212-
angular.module('bootstrap', []).directive(directive);
383+
angular.module('bootstrap', []).directive(directive).factory('popoverElement', popoverElement);

docs/content/notes/empty.tmp

Whitespace-only changes.

0 commit comments

Comments
 (0)