erlapi 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +15 -0
  5. data/Rakefile +56 -0
  6. data/VERSION +1 -0
  7. data/bin/erlapi +26 -0
  8. data/erlapi.gemspec +140 -0
  9. data/lib/erlapi.rb +38 -0
  10. data/lib/erlapi/generator.rb +194 -0
  11. data/lib/erlapi/generator/shtml.rb +353 -0
  12. data/lib/erlapi/generator/template/direct/_context.rhtml +172 -0
  13. data/lib/erlapi/generator/template/direct/class.rhtml +40 -0
  14. data/lib/erlapi/generator/template/direct/file.rhtml +30 -0
  15. data/lib/erlapi/generator/template/direct/index.rhtml +14 -0
  16. data/lib/erlapi/generator/template/direct/resources/apple-touch-icon.png +0 -0
  17. data/lib/erlapi/generator/template/direct/resources/css/main.css +263 -0
  18. data/lib/erlapi/generator/template/direct/resources/css/panel.css +383 -0
  19. data/lib/erlapi/generator/template/direct/resources/css/reset.css +53 -0
  20. data/lib/erlapi/generator/template/direct/resources/favicon.ico +0 -0
  21. data/lib/erlapi/generator/template/direct/resources/i/arrows.png +0 -0
  22. data/lib/erlapi/generator/template/direct/resources/i/results_bg.png +0 -0
  23. data/lib/erlapi/generator/template/direct/resources/i/tree_bg.png +0 -0
  24. data/lib/erlapi/generator/template/direct/resources/js/jquery-1.3.2.min.js +19 -0
  25. data/lib/erlapi/generator/template/direct/resources/js/jquery-effect.js +593 -0
  26. data/lib/erlapi/generator/template/direct/resources/js/main.js +22 -0
  27. data/lib/erlapi/generator/template/direct/resources/js/searchdoc.js +628 -0
  28. data/lib/erlapi/generator/template/direct/resources/panel/index.html +71 -0
  29. data/lib/erlapi/generator/template/merge/index.rhtml +14 -0
  30. data/lib/erlapi/generator/template/shtml/_context.rhtml +164 -0
  31. data/lib/erlapi/generator/template/shtml/class.rhtml +46 -0
  32. data/lib/erlapi/generator/template/shtml/file.rhtml +37 -0
  33. data/lib/erlapi/generator/template/shtml/index.rhtml +14 -0
  34. data/lib/erlapi/generator/template/shtml/resources/apple-touch-icon.png +0 -0
  35. data/lib/erlapi/generator/template/shtml/resources/css/main.css +191 -0
  36. data/lib/erlapi/generator/template/shtml/resources/css/panel.css +383 -0
  37. data/lib/erlapi/generator/template/shtml/resources/css/reset.css +53 -0
  38. data/lib/erlapi/generator/template/shtml/resources/favicon.ico +0 -0
  39. data/lib/erlapi/generator/template/shtml/resources/i/arrows.png +0 -0
  40. data/lib/erlapi/generator/template/shtml/resources/i/results_bg.png +0 -0
  41. data/lib/erlapi/generator/template/shtml/resources/i/tree_bg.png +0 -0
  42. data/lib/erlapi/generator/template/shtml/resources/js/jquery-1.3.2.min.js +19 -0
  43. data/lib/erlapi/generator/template/shtml/resources/js/main.js +34 -0
  44. data/lib/erlapi/generator/template/shtml/resources/js/searchdoc.js +628 -0
  45. data/lib/erlapi/generator/template/shtml/resources/panel/index.html +71 -0
  46. data/lib/erlapi/helpers.rb +26 -0
  47. data/lib/erlapi/merge.rb +217 -0
  48. data/lib/erlapi/parser.rb +134 -0
  49. data/lib/erlapi/shtml.rb +351 -0
  50. data/lib/erlapi/templatable.rb +51 -0
  51. data/lib/erlapi/templates/html/direct/_context.rhtml +172 -0
  52. data/lib/erlapi/templates/html/direct/class.rhtml +40 -0
  53. data/lib/erlapi/templates/html/direct/file.rhtml +30 -0
  54. data/lib/erlapi/templates/html/direct/index.rhtml +14 -0
  55. data/lib/erlapi/templates/html/direct/resources/apple-touch-icon.png +0 -0
  56. data/lib/erlapi/templates/html/direct/resources/css/main.css +263 -0
  57. data/lib/erlapi/templates/html/direct/resources/css/panel.css +383 -0
  58. data/lib/erlapi/templates/html/direct/resources/css/reset.css +53 -0
  59. data/lib/erlapi/templates/html/direct/resources/favicon.ico +0 -0
  60. data/lib/erlapi/templates/html/direct/resources/i/arrows.png +0 -0
  61. data/lib/erlapi/templates/html/direct/resources/i/results_bg.png +0 -0
  62. data/lib/erlapi/templates/html/direct/resources/i/tree_bg.png +0 -0
  63. data/lib/erlapi/templates/html/direct/resources/js/jquery-1.3.2.min.js +19 -0
  64. data/lib/erlapi/templates/html/direct/resources/js/jquery-effect.js +593 -0
  65. data/lib/erlapi/templates/html/direct/resources/js/main.js +22 -0
  66. data/lib/erlapi/templates/html/direct/resources/js/searchdoc.js +628 -0
  67. data/lib/erlapi/templates/html/direct/resources/panel/index.html +71 -0
  68. data/lib/erlapi/templates/html/merge/index.rhtml +14 -0
  69. data/lib/erlapi/templates/html/shtml/_context.rhtml +68 -0
  70. data/lib/erlapi/templates/html/shtml/class.rhtml +25 -0
  71. data/lib/erlapi/templates/html/shtml/file.rhtml +37 -0
  72. data/lib/erlapi/templates/html/shtml/index.rhtml +14 -0
  73. data/lib/erlapi/templates/html/shtml/resources copy/apple-touch-icon.png +0 -0
  74. data/lib/erlapi/templates/html/shtml/resources copy/css/main.css +195 -0
  75. data/lib/erlapi/templates/html/shtml/resources copy/css/panel.css +383 -0
  76. data/lib/erlapi/templates/html/shtml/resources copy/css/reset.css +53 -0
  77. data/lib/erlapi/templates/html/shtml/resources copy/favicon.ico +0 -0
  78. data/lib/erlapi/templates/html/shtml/resources copy/i/arrows.png +0 -0
  79. data/lib/erlapi/templates/html/shtml/resources copy/i/results_bg.png +0 -0
  80. data/lib/erlapi/templates/html/shtml/resources copy/i/tree_bg.png +0 -0
  81. data/lib/erlapi/templates/html/shtml/resources copy/js/jquery-1.3.2.min.js +19 -0
  82. data/lib/erlapi/templates/html/shtml/resources copy/js/main.js +34 -0
  83. data/lib/erlapi/templates/html/shtml/resources copy/js/searchdoc.js +628 -0
  84. data/lib/erlapi/templates/html/shtml/resources copy/panel/index.html +71 -0
  85. data/lib/erlapi/templates/html/shtml/resources/apple-touch-icon.png +0 -0
  86. data/lib/erlapi/templates/html/shtml/resources/css/main.css +263 -0
  87. data/lib/erlapi/templates/html/shtml/resources/css/panel.css +383 -0
  88. data/lib/erlapi/templates/html/shtml/resources/css/reset.css +53 -0
  89. data/lib/erlapi/templates/html/shtml/resources/favicon.ico +0 -0
  90. data/lib/erlapi/templates/html/shtml/resources/i/arrows.png +0 -0
  91. data/lib/erlapi/templates/html/shtml/resources/i/results_bg.png +0 -0
  92. data/lib/erlapi/templates/html/shtml/resources/i/tree_bg.png +0 -0
  93. data/lib/erlapi/templates/html/shtml/resources/js/jquery-1.3.2.min.js +19 -0
  94. data/lib/erlapi/templates/html/shtml/resources/js/jquery-effect.js +593 -0
  95. data/lib/erlapi/templates/html/shtml/resources/js/main.js +22 -0
  96. data/lib/erlapi/templates/html/shtml/resources/js/searchdoc.js +620 -0
  97. data/lib/erlapi/templates/html/shtml/resources/panel/index.html +71 -0
  98. data/test/erlapi_test.rb +7 -0
  99. data/test/test_helper.rb +10 -0
  100. metadata +155 -0
@@ -0,0 +1,22 @@
1
+ function toggleSource( id )
2
+ {
3
+ var $src = $('#' + id).toggle();
4
+ $('#l_' + id).html($src.css('display') == 'none' ? 'show' : 'hide');
5
+ }
6
+
7
+ function openCode( url )
8
+ {
9
+ window.open( url, "SOURCE_CODE", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=480,width=750" ).focus();
10
+ }
11
+
12
+
13
+ window.highlight = function(url) {
14
+ var hash = url.match(/#([^#]+)$/)
15
+ if(hash) {
16
+ $('a[name=' + hash[1] + ']').parent().effect('highlight', {}, 'slow')
17
+ }
18
+ }
19
+
20
+ $(function() {
21
+ highlight('#' + location.hash);
22
+ });
@@ -0,0 +1,620 @@
1
+ Searchdoc = {};
2
+
3
+ // navigation.js ------------------------------------------
4
+
5
+ Searchdoc.Navigation = new function() {
6
+ this.initNavigation = function() {
7
+ var _this = this;
8
+
9
+ $(document).keydown(function(e) {
10
+ _this.onkeydown(e);
11
+ }).keyup(function(e) {
12
+ _this.onkeyup(e);
13
+ });
14
+
15
+ this.navigationActive = true;
16
+ }
17
+
18
+ this.setNavigationActive = function(state) {
19
+ this.navigationActive = state;
20
+ this.clearMoveTimeout();
21
+ }
22
+
23
+
24
+ this.onkeyup = function(e) {
25
+ if (!this.navigationActive) return;
26
+ switch(e.keyCode) {
27
+ case 37: //Event.KEY_LEFT:
28
+ case 38: //Event.KEY_UP:
29
+ case 39: //Event.KEY_RIGHT:
30
+ case 40: //Event.KEY_DOWN:
31
+ case 73: // i
32
+ case 67: // c (dvorak)
33
+ case 75: // k
34
+ case 84: // t (dvorak)
35
+ this.clearMoveTimeout();
36
+ break;
37
+ }
38
+ }
39
+
40
+ this.onkeydown = function(e) {
41
+ if (!this.navigationActive) return;
42
+ switch(e.keyCode) {
43
+ case 37: //Event.KEY_LEFT:
44
+ if (this.moveLeft()) e.preventDefault();
45
+ break;
46
+ case 38: //Event.KEY_UP:
47
+ case 73: // i
48
+ case 67: // c (dvorak)
49
+ if (e.keyCode == 38 || e.ctrlKey) {
50
+ if (this.moveUp()) e.preventDefault();
51
+ this.startMoveTimeout(false);
52
+ }
53
+ break;
54
+ case 39: //Event.KEY_RIGHT:
55
+ if (this.moveRight()) e.preventDefault();
56
+ break;
57
+ case 40: //Event.KEY_DOWN:
58
+ case 75: // k
59
+ case 84: // t (dvorak)
60
+ if (e.keyCode == 40 || e.ctrlKey) {
61
+ if (this.moveDown()) e.preventDefault();
62
+ this.startMoveTimeout(true);
63
+ }
64
+ break;
65
+ case 9: //Event.KEY_TAB:
66
+ case 13: //Event.KEY_RETURN:
67
+ if (this.$current) this.select(this.$current);
68
+ break;
69
+ }
70
+ if (e.ctrlKey && e.shiftKey) this.select(this.$current);
71
+ }
72
+
73
+ this.clearMoveTimeout = function() {
74
+ clearTimeout(this.moveTimeout);
75
+ this.moveTimeout = null;
76
+ }
77
+
78
+ this.startMoveTimeout = function(isDown) {
79
+ if (!$.browser.mozilla && !$.browser.opera) return;
80
+ if (this.moveTimeout) this.clearMoveTimeout();
81
+ var _this = this;
82
+
83
+ var go = function() {
84
+ if (!_this.moveTimeout) return;
85
+ _this[isDown ? 'moveDown' : 'moveUp']();
86
+ _this.moveTimout = setTimeout(go, 100);
87
+ }
88
+ this.moveTimeout = setTimeout(go, 200);
89
+ }
90
+
91
+ this.moveRight = function() {
92
+ }
93
+
94
+ this.moveLeft = function() {
95
+ }
96
+
97
+ this.move = function(isDown) {
98
+ }
99
+
100
+ this.moveUp = function() {
101
+ return this.move(false);
102
+ }
103
+
104
+ this.moveDown = function() {
105
+ return this.move(true);
106
+ }
107
+ }
108
+
109
+
110
+ // scrollIntoView.js --------------------------------------
111
+
112
+ function scrollIntoView(element, view) {
113
+ var offset, viewHeight, viewScroll, height;
114
+ offset = element.offsetTop;
115
+ height = element.offsetHeight;
116
+ viewHeight = view.offsetHeight;
117
+ viewScroll = view.scrollTop;
118
+ if (offset - viewScroll + height > viewHeight) {
119
+ view.scrollTop = offset - viewHeight + height;
120
+ }
121
+ if (offset < viewScroll) {
122
+ view.scrollTop = offset;
123
+ }
124
+ }
125
+
126
+
127
+ // searcher.js --------------------------------------------
128
+
129
+ Searchdoc.Searcher = function(data) {
130
+ this.data = data;
131
+ this.handlers = [];
132
+ }
133
+
134
+ Searchdoc.Searcher.prototype = new function() {
135
+ var CHUNK_SIZE = 1000, // search is performed in chunks of 1000 for non-bloking user input
136
+ MAX_RESULTS = 100, // do not try to find more than 100 results
137
+ huid = 1, suid = 1,
138
+ runs = 0;
139
+
140
+
141
+ this.find = function(query) {
142
+ var queries = splitQuery(query),
143
+ regexps = buildRegexps(queries),
144
+ highlighters = buildHilighters(queries),
145
+ state = { from: 0, pass: 0, limit: MAX_RESULTS, n: suid++},
146
+ _this = this;
147
+ this.currentSuid = state.n;
148
+
149
+ if (!query) return;
150
+
151
+ var run = function() {
152
+ // stop current search thread if new search started
153
+ if (state.n != _this.currentSuid) return;
154
+
155
+ var results = performSearch(_this.data, regexps, queries, highlighters, state),
156
+ hasMore = (state.limit > 0 && state.pass < 3);
157
+
158
+ triggerResults.call(_this, results, !hasMore);
159
+ if (hasMore) {
160
+ setTimeout(run, 2);
161
+ }
162
+ runs++;
163
+ };
164
+ runs = 0;
165
+
166
+ // start search thread
167
+ run();
168
+ }
169
+
170
+ /* ----- Events ------ */
171
+ this.ready = function(fn) {
172
+ fn.huid = huid;
173
+ this.handlers.push(fn);
174
+ }
175
+
176
+ /* ----- Utilities ------ */
177
+ function splitQuery(query) {
178
+ return jQuery.grep(query.split(/(\s+|\(\)?)/), function(string) { return string.match(/\S/) });
179
+ }
180
+
181
+ function buildRegexps(queries) {
182
+ return jQuery.map(queries, function(query) { return new RegExp(query.replace(/(.)/g, '([$1])([^$1]*?)'), 'i') });
183
+ }
184
+
185
+ function buildHilighters(queries) {
186
+ return jQuery.map(queries, function(query) {
187
+ return jQuery.map( query.split(''), function(l, i){ return '\u0001$' + (i*2+1) + '\u0002$' + (i*2+2) } ).join('')
188
+ });
189
+ }
190
+
191
+ // function longMatchRegexp(index, longIndex, regexps) {
192
+ // for (var i = regexps.length - 1; i >= 0; i--){
193
+ // if (!index.match(regexps[i]) && !longIndex.match(regexps[i])) return false;
194
+ // };
195
+ // return true;
196
+ // }
197
+
198
+
199
+ /* ----- Mathchers ------ */
200
+ function matchPass1(index, longIndex, queries, regexps) {
201
+ if (index.indexOf(queries[0]) != 0) return false;
202
+ for (var i=1, l = regexps.length; i < l; i++) {
203
+ if (!index.match(regexps[i]) && !longIndex.match(regexps[i])) return false;
204
+ };
205
+ return true;
206
+ }
207
+
208
+ function matchPass2(index, longIndex, queries, regexps) {
209
+ if (index.indexOf(queries[0]) == -1) return false;
210
+ for (var i=1, l = regexps.length; i < l; i++) {
211
+ if (!index.match(regexps[i]) && !longIndex.match(regexps[i])) return false;
212
+ };
213
+ return true;
214
+ }
215
+
216
+ function matchPassRegexp(index, longIndex, queries, regexps) {
217
+ if (!index.match(regexps[0])) return false;
218
+ for (var i=1, l = regexps.length; i < l; i++) {
219
+ if (!index.match(regexps[i]) && !longIndex.match(regexps[i])) return false;
220
+ };
221
+ return true;
222
+ }
223
+
224
+
225
+ /* ----- Highlighters ------ */
226
+ function highlightRegexp(info, queries, regexps, highlighters) {
227
+ var result = createResult(info);
228
+ for (var i=0, l = regexps.length; i < l; i++) {
229
+ result.title = result.title.replace(regexps[i], highlighters[i]);
230
+ if (i > 0)
231
+ result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
232
+ };
233
+ return result;
234
+ }
235
+
236
+ function hltSubstring(string, pos, length) {
237
+ return string.substring(0, pos) + '\u0001' + string.substring(pos, pos + length) + '\u0002' + string.substring(pos + length);
238
+ }
239
+
240
+ function highlightQuery(info, queries, regexps, highlighters) {
241
+ var result = createResult(info), pos = 0, lcTitle = result.title.toLowerCase();
242
+ pos = lcTitle.indexOf(queries[0]);
243
+ if (pos != -1) {
244
+ result.title = hltSubstring(result.title, pos, queries[0].length);
245
+ }
246
+ for (var i=1, l = regexps.length; i < l; i++) {
247
+ result.title = result.title.replace(regexps[i], highlighters[i]);
248
+ result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
249
+ };
250
+ return result;
251
+ }
252
+
253
+ function createResult(info) {
254
+ var result = {};
255
+ result.title = info[0];
256
+ result.namespace = info[1];
257
+ result.path = info[2];
258
+ result.params = info[3];
259
+ result.snippet = info[4];
260
+ result.badge = info[6];
261
+ return result;
262
+ }
263
+
264
+ /* ----- Searching ------ */
265
+ function performSearch(data, regexps, queries, highlighters, state) {
266
+ var searchIndex = data.searchIndex, // search by title first and then by source
267
+ longSearchIndex = data.longSearchIndex,
268
+ info = data.info,
269
+ result = [],
270
+ i = state.from,
271
+ l = searchIndex.length,
272
+ togo = CHUNK_SIZE,
273
+ matchFunc, hltFunc;
274
+
275
+ while (state.pass < 3 && state.limit > 0 && togo > 0) {
276
+ if (state.pass == 0) {
277
+ matchFunc = matchPass1;
278
+ hltFunc = highlightQuery;
279
+ } else if (state.pass == 1) {
280
+ matchFunc = matchPass2;
281
+ hltFunc = highlightQuery;
282
+ } else if (state.pass == 2) {
283
+ matchFunc = matchPassRegexp;
284
+ hltFunc = highlightRegexp;
285
+ }
286
+
287
+ for (; togo > 0 && i < l && state.limit > 0; i++, togo--) {
288
+ if (info[i].n == state.n) continue;
289
+ if (matchFunc(searchIndex[i], longSearchIndex[i], queries, regexps)) {
290
+ info[i].n = state.n;
291
+ result.push(hltFunc(info[i], queries, regexps, highlighters));
292
+ state.limit--;
293
+ }
294
+ };
295
+ if (searchIndex.length <= i) {
296
+ state.pass++;
297
+ i = state.from = 0;
298
+ } else {
299
+ state.from = i;
300
+ }
301
+ }
302
+ return result;
303
+ }
304
+
305
+ function triggerResults(results, isLast) {
306
+ jQuery.each(this.handlers, function(i, fn) { fn.call(this, results, isLast) })
307
+ }
308
+ }
309
+
310
+
311
+
312
+
313
+ // panel.js -----------------------------------------------
314
+
315
+ Searchdoc.Panel = function(element, data, tree, frame) {
316
+ this.$element = $(element);
317
+ this.$input = $('input', element).eq(0);
318
+ this.$result = $('.result ul', element).eq(0);
319
+ this.frame = frame;
320
+ this.$current = null;
321
+ this.$view = this.$result.parent();
322
+ this.data = data;
323
+ this.searcher = new Searchdoc.Searcher(data.index);
324
+ this.tree = new Searchdoc.Tree($('.tree', element), tree, this);
325
+ this.init();
326
+ }
327
+
328
+ Searchdoc.Panel.prototype = $.extend({}, Searchdoc.Navigation, new function() {
329
+ var suid = 1;
330
+
331
+ this.init = function() {
332
+ var _this = this;
333
+ var observer = function() {
334
+ _this.search(_this.$input[0].value);
335
+ };
336
+ this.$input.keyup(observer);
337
+ this.$input.click(observer); // mac's clear field
338
+
339
+ this.searcher.ready(function(results, isLast) {
340
+ _this.addResults(results, isLast);
341
+ })
342
+
343
+ this.$result.click(function(e) {
344
+ _this.$current.removeClass('current');
345
+ _this.$current = $(e.target).closest('li').addClass('current');
346
+ _this.select();
347
+ _this.$input.focus();
348
+ });
349
+
350
+ this.initNavigation();
351
+ this.setNavigationActive(false);
352
+ }
353
+
354
+ this.search = function(value, selectFirstMatch) {
355
+ value = jQuery.trim(value).toLowerCase();
356
+ this.selectFirstMatch = selectFirstMatch;
357
+ if (value) {
358
+ this.$element.removeClass('panel_tree').addClass('panel_results');
359
+ this.tree.setNavigationActive(false);
360
+ this.setNavigationActive(true);
361
+ } else {
362
+ this.$element.addClass('panel_tree').removeClass('panel_results');
363
+ this.tree.setNavigationActive(true);
364
+ this.setNavigationActive(false);
365
+ }
366
+ if (value != this.lastQuery) {
367
+ this.lastQuery = value;
368
+ this.firstRun = true;
369
+ this.searcher.find(value);
370
+ }
371
+ }
372
+
373
+ this.addResults = function(results, isLast) {
374
+ var target = this.$result.get(0);
375
+ if (this.firstRun && (results.length > 0 || isLast)) {
376
+ this.$current = null;
377
+ this.$result.empty();
378
+ }
379
+ for (var i=0, l = results.length; i < l; i++) {
380
+ target.appendChild(renderItem.call(this, results[i]));
381
+ };
382
+ if (this.firstRun && results.length > 0) {
383
+ this.firstRun = false;
384
+ this.$current = $(target.firstChild);
385
+ this.$current.addClass('current');
386
+ if (this.selectFirstMatch) this.select();
387
+ scrollIntoView(this.$current[0], this.$view[0])
388
+ }
389
+ if (jQuery.browser.msie) this.$element[0].className += '';
390
+ }
391
+
392
+ this.open = function(src) {
393
+ this.frame.location.href = '../' + src;
394
+ if (this.frame.highlight) this.frame.highlight(src);
395
+ }
396
+
397
+ this.select = function() {
398
+ this.open(this.$current.data('path'));
399
+ }
400
+
401
+ this.move = function(isDown) {
402
+ if (!this.$current) return;
403
+ var $next = this.$current[isDown ? 'next' : 'prev']();
404
+ if ($next.length) {
405
+ this.$current.removeClass('current');
406
+ $next.addClass('current');
407
+ scrollIntoView($next[0], this.$view[0]);
408
+ this.$current = $next;
409
+ }
410
+ return true;
411
+ }
412
+
413
+ function renderItem(result) {
414
+ var li = document.createElement('li'),
415
+ html = '', badge = result.badge;
416
+ html += '<h1>' + hlt(result.title);
417
+ if (result.params) html += '<i>' + result.params + '</i>';
418
+ html += '</h1>';
419
+ html += '<p>';
420
+ if (typeof badge != 'undefined') {
421
+ html += '<span class="badge badge_' + (badge % 6 + 1) + '">' + escapeHTML(this.data.badges[badge] || 'unknown') + '</span>';
422
+ }
423
+ html += hlt(result.namespace) + '</p>';
424
+ if (result.snippet) html += '<p class="snippet">' + escapeHTML(result.snippet) + '</p>';
425
+ li.innerHTML = html;
426
+ jQuery.data(li, 'path', result.path);
427
+ return li;
428
+ }
429
+
430
+ function hlt(html) {
431
+ return escapeHTML(html).replace(/\u0001/g, '<b>').replace(/\u0002/g, '</b>')
432
+ }
433
+
434
+ function escapeHTML(html) {
435
+ return html.replace(/[&<>]/g, function(c) {
436
+ return '&#' + c.charCodeAt(0) + ';';
437
+ });
438
+ }
439
+
440
+ });
441
+
442
+ // tree.js ------------------------------------------------
443
+
444
+ Searchdoc.Tree = function(element, tree, panel) {
445
+ this.$element = $(element);
446
+ this.$list = $('ul', element);
447
+ this.tree = tree;
448
+ this.panel = panel;
449
+ this.init();
450
+ }
451
+
452
+ Searchdoc.Tree.prototype = $.extend({}, Searchdoc.Navigation, new function() {
453
+ this.init = function() {
454
+ var stopper = document.createElement('li');
455
+ stopper.className = 'stopper';
456
+ this.$list[0].appendChild(stopper);
457
+ for (var i=0, l = this.tree.length; i < l; i++) {
458
+ buildAndAppendItem.call(this, this.tree[i], 0, stopper);
459
+ };
460
+ var _this = this;
461
+ this.$list.click(function(e) {
462
+ var $target = $(e.target),
463
+ $li = $target.closest('li');
464
+ if ($target.hasClass('icon')) {
465
+ _this.toggle($li);
466
+ } else {
467
+ _this.select($li);
468
+ }
469
+ })
470
+
471
+ this.initNavigation();
472
+ if (jQuery.browser.msie) document.body.className += '';
473
+ }
474
+
475
+ this.select = function($li) {
476
+ this.highlight($li);
477
+ var path = $li[0].searchdoc_tree_data.path;
478
+ if (path) this.panel.open(path);
479
+ }
480
+
481
+ this.highlight = function($li) {
482
+ if (this.$current) this.$current.removeClass('current');
483
+ this.$current = $li.addClass('current');
484
+ }
485
+
486
+ this.toggle = function($li) {
487
+ var closed = !$li.hasClass('closed'),
488
+ children = $li[0].searchdoc_tree_data.children;
489
+ $li.toggleClass('closed');
490
+ for (var i=0, l = children.length; i < l; i++) {
491
+ toggleVis.call(this, $(children[i].li), !closed);
492
+ };
493
+ }
494
+
495
+ this.moveRight = function() {
496
+ if (!this.$current) {
497
+ this.highlight(this.$list.find('li:first'));
498
+ return;
499
+ }
500
+ if (this.$current.hasClass('closed')) {
501
+ this.toggle(this.$current);
502
+ }
503
+ }
504
+
505
+ this.moveLeft = function() {
506
+ if (!this.$current) {
507
+ this.highlight(this.$list.find('li:first'));
508
+ return;
509
+ }
510
+ if (!this.$current.hasClass('closed')) {
511
+ this.toggle(this.$current);
512
+ } else {
513
+ var level = this.$current[0].searchdoc_tree_data.level;
514
+ if (level == 0) return;
515
+ var $next = this.$current.prevAll('li.level_' + (level - 1) + ':visible:first');
516
+ this.$current.removeClass('current');
517
+ $next.addClass('current');
518
+ scrollIntoView($next[0], this.$element[0]);
519
+ this.$current = $next;
520
+ }
521
+ }
522
+
523
+ this.move = function(isDown) {
524
+ if (!this.$current) {
525
+ this.highlight(this.$list.find('li:first'));
526
+ return true;
527
+ }
528
+ var next = this.$current[0];
529
+ if (isDown) {
530
+ do {
531
+ next = next.nextSibling;
532
+ if (next && next.style && next.style.display != 'none') break;
533
+ } while(next);
534
+ } else {
535
+ do {
536
+ next = next.previousSibling;
537
+ if (next && next.style && next.style.display != 'none') break;
538
+ } while(next);
539
+ }
540
+ if (next && next.className.indexOf('stopper') == -1) {
541
+ this.$current.removeClass('current');
542
+ $(next).addClass('current');
543
+ scrollIntoView(next, this.$element[0]);
544
+ this.$current = $(next);
545
+ }
546
+ return true;
547
+ }
548
+
549
+ function toggleVis($li, show) {
550
+ var closed = $li.hasClass('closed'),
551
+ children = $li[0].searchdoc_tree_data.children;
552
+ $li.css('display', show ? '' : 'none')
553
+ if (!show && this.$current && $li[0] == this.$current[0]) {
554
+ this.$current.removeClass('current');
555
+ this.$current = null;
556
+ }
557
+ for (var i=0, l = children.length; i < l; i++) {
558
+ toggleVis.call(this, $(children[i].li), show && !closed);
559
+ };
560
+ }
561
+
562
+ function buildAndAppendItem(item, level, before) {
563
+ var li = renderItem(item, level),
564
+ list = this.$list[0];
565
+ item.li = li;
566
+ list.insertBefore(li, before);
567
+ for (var i=0, l = item[3].length; i < l; i++) {
568
+ buildAndAppendItem.call(this, item[3][i], level + 1, before);
569
+ };
570
+ return li;
571
+ }
572
+
573
+ function renderItem(item, level) {
574
+ var li = document.createElement('li'),
575
+ cnt = document.createElement('div'),
576
+ h1 = document.createElement('h1'),
577
+ p = document.createElement('p'),
578
+ icon, i;
579
+
580
+ li.appendChild(cnt);
581
+ li.style.paddingLeft = getOffset(level);
582
+ cnt.className = 'content';
583
+ if (!item[1]) li.className = 'empty ';
584
+ cnt.appendChild(h1);
585
+ // cnt.appendChild(p);
586
+ h1.appendChild(document.createTextNode(item[0]));
587
+ // p.appendChild(document.createTextNode(item[4]));
588
+ if (item[2]) {
589
+ i = document.createElement('i');
590
+ i.appendChild(document.createTextNode(item[2]));
591
+ h1.appendChild(i);
592
+ }
593
+ if (item[3].length > 0) {
594
+ icon = document.createElement('div');
595
+ icon.className = 'icon';
596
+ cnt.appendChild(icon);
597
+ }
598
+
599
+ // user direct assignement instead of $()
600
+ // it's 8x faster
601
+ // $(li).data('path', item[1])
602
+ // .data('children', item[3])
603
+ // .data('level', level)
604
+ // .css('display', level == 0 ? '' : 'none')
605
+ // .addClass('level_' + level)
606
+ // .addClass('closed');
607
+ li.searchdoc_tree_data = {
608
+ path: item[1],
609
+ children: item[3],
610
+ level: level
611
+ }
612
+ li.style.display = level == 0 ? '' : 'none';
613
+ li.className += 'level_' + level + ' closed';
614
+ return li;
615
+ }
616
+
617
+ function getOffset(level) {
618
+ return 5 + 18*level + 'px';
619
+ }
620
+ });