rdoc 6.15.0 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +187 -0
- data/History.rdoc +1 -1
- data/LEGAL.rdoc +6 -0
- data/README.md +20 -3
- data/lib/rdoc/code_object/any_method.rb +15 -7
- data/lib/rdoc/code_object/class_module.rb +13 -0
- data/lib/rdoc/code_object/constant.rb +9 -0
- data/lib/rdoc/code_object/method_attr.rb +13 -1
- data/lib/rdoc/code_object/top_level.rb +31 -18
- data/lib/rdoc/comment.rb +190 -8
- data/lib/rdoc/generator/aliki.rb +183 -0
- data/lib/rdoc/generator/darkfish.rb +5 -1
- data/lib/rdoc/generator/template/aliki/_aside_toc.rhtml +8 -0
- data/lib/rdoc/generator/template/aliki/_footer.rhtml +23 -0
- data/lib/rdoc/generator/template/aliki/_head.rhtml +158 -0
- data/lib/rdoc/generator/template/aliki/_header.rhtml +56 -0
- data/lib/rdoc/generator/template/aliki/_icons.rhtml +208 -0
- data/lib/rdoc/generator/template/aliki/_sidebar_ancestors.rhtml +16 -0
- data/lib/rdoc/generator/template/aliki/_sidebar_classes.rhtml +15 -0
- data/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml +25 -0
- data/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml +25 -0
- data/lib/rdoc/generator/template/aliki/_sidebar_installed.rhtml +16 -0
- data/lib/rdoc/generator/template/aliki/_sidebar_methods.rhtml +41 -0
- data/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml +67 -0
- data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +15 -0
- data/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml +21 -0
- data/lib/rdoc/generator/template/aliki/_sidebar_toggle.rhtml +3 -0
- data/lib/rdoc/generator/template/aliki/class.rhtml +218 -0
- data/lib/rdoc/generator/template/aliki/css/rdoc.css +1944 -0
- data/lib/rdoc/generator/template/aliki/index.rhtml +22 -0
- data/lib/rdoc/generator/template/aliki/js/aliki.js +498 -0
- data/lib/rdoc/generator/template/aliki/js/c_highlighter.js +299 -0
- data/lib/rdoc/generator/template/aliki/js/search_controller.js +120 -0
- data/lib/rdoc/generator/template/aliki/js/search_navigation.js +105 -0
- data/lib/rdoc/generator/template/aliki/js/search_ranker.js +239 -0
- data/lib/rdoc/generator/template/aliki/js/theme-toggle.js +112 -0
- data/lib/rdoc/generator/template/aliki/page.rhtml +18 -0
- data/lib/rdoc/generator/template/aliki/servlet_not_found.rhtml +14 -0
- data/lib/rdoc/generator/template/aliki/servlet_root.rhtml +65 -0
- data/lib/rdoc/generator/template/darkfish/_footer.rhtml +3 -3
- data/lib/rdoc/generator/template/darkfish/_head.rhtml +14 -19
- data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +8 -8
- data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +8 -8
- data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +7 -6
- data/lib/rdoc/generator/template/darkfish/_sidebar_methods.rhtml +6 -6
- data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +19 -19
- data/lib/rdoc/generator/template/darkfish/_sidebar_parent.rhtml +2 -2
- data/lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml +1 -0
- data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +3 -3
- data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +14 -14
- data/lib/rdoc/generator/template/darkfish/class.rhtml +62 -62
- data/lib/rdoc/generator/template/darkfish/index.rhtml +4 -3
- data/lib/rdoc/generator/template/darkfish/page.rhtml +2 -1
- data/lib/rdoc/generator/template/darkfish/servlet_not_found.rhtml +2 -1
- data/lib/rdoc/generator/template/darkfish/servlet_root.rhtml +19 -19
- data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +19 -17
- data/lib/rdoc/generator/template/json_index/js/searcher.js +43 -6
- data/lib/rdoc/generator.rb +1 -0
- data/lib/rdoc/markup/pre_process.rb +34 -10
- data/lib/rdoc/markup/to_ansi.rb +4 -0
- data/lib/rdoc/markup/to_bs.rb +4 -0
- data/lib/rdoc/markup/to_html.rb +6 -4
- data/lib/rdoc/markup/to_rdoc.rb +11 -3
- data/lib/rdoc/options.rb +21 -10
- data/lib/rdoc/parser/c.rb +15 -46
- data/lib/rdoc/parser/prism_ruby.rb +121 -113
- data/lib/rdoc/parser/ruby.rb +8 -8
- data/lib/rdoc/parser/ruby_tools.rb +5 -7
- data/lib/rdoc/parser/simple.rb +4 -21
- data/lib/rdoc/rdoc.rb +1 -0
- data/lib/rdoc/rubygems_hook.rb +3 -3
- data/lib/rdoc/text.rb +1 -1
- data/lib/rdoc/token_stream.rb +13 -1
- data/lib/rdoc/tom_doc.rb +1 -1
- data/lib/rdoc/version.rb +1 -1
- data/rdoc.gemspec +1 -1
- metadata +33 -5
- data/CONTRIBUTING.rdoc +0 -219
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side C syntax highlighter for RDoc
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
(function() {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
// C control flow and storage class keywords
|
|
9
|
+
const C_KEYWORDS = new Set([
|
|
10
|
+
'auto', 'break', 'case', 'continue', 'default', 'do', 'else', 'extern',
|
|
11
|
+
'for', 'goto', 'if', 'inline', 'register', 'return', 'sizeof', 'static',
|
|
12
|
+
'switch', 'while',
|
|
13
|
+
'_Alignas', '_Alignof', '_Generic', '_Noreturn', '_Static_assert', '_Thread_local'
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
// C type keywords and type qualifiers
|
|
17
|
+
const C_TYPE_KEYWORDS = new Set([
|
|
18
|
+
'bool', 'char', 'const', 'double', 'enum', 'float', 'int', 'long',
|
|
19
|
+
'restrict', 'short', 'signed', 'struct', 'typedef', 'union', 'unsigned',
|
|
20
|
+
'void', 'volatile', '_Atomic', '_Bool', '_Complex', '_Imaginary'
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
// Library-defined types (typedef'd in headers, not language keywords)
|
|
24
|
+
// Includes: Ruby C API types (VALUE, ID), POSIX types (size_t, ssize_t),
|
|
25
|
+
// fixed-width integer types (uint32_t, int64_t), and standard I/O types (FILE)
|
|
26
|
+
const C_TYPES = new Set([
|
|
27
|
+
'VALUE', 'ID', 'size_t', 'ssize_t', 'ptrdiff_t', 'uintptr_t', 'intptr_t',
|
|
28
|
+
'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t',
|
|
29
|
+
'int8_t', 'int16_t', 'int32_t', 'int64_t',
|
|
30
|
+
'FILE', 'DIR', 'va_list'
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
// Common Ruby VALUE macros and boolean literals
|
|
34
|
+
const RUBY_MACROS = new Set([
|
|
35
|
+
'Qtrue', 'Qfalse', 'Qnil', 'Qundef', 'NULL', 'TRUE', 'FALSE', 'true', 'false'
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
const OPERATORS = new Set([
|
|
39
|
+
'==', '!=', '<=', '>=', '&&', '||', '<<', '>>', '++', '--',
|
|
40
|
+
'+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '->',
|
|
41
|
+
'+', '-', '*', '/', '%', '<', '>', '=', '!', '&', '|', '^', '~'
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
// Single character that can start an operator
|
|
45
|
+
const OPERATOR_CHARS = new Set('+-*/%<>=!&|^~');
|
|
46
|
+
|
|
47
|
+
function isMacro(word) {
|
|
48
|
+
return RUBY_MACROS.has(word) || /^[A-Z][A-Z0-9_]*$/.test(word);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isType(word) {
|
|
52
|
+
return C_TYPE_KEYWORDS.has(word) || C_TYPES.has(word) || /_t$/.test(word);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Escape HTML special characters
|
|
57
|
+
*/
|
|
58
|
+
function escapeHtml(text) {
|
|
59
|
+
return text
|
|
60
|
+
.replace(/&/g, '&')
|
|
61
|
+
.replace(/</g, '<')
|
|
62
|
+
.replace(/>/g, '>')
|
|
63
|
+
.replace(/"/g, '"')
|
|
64
|
+
.replace(/'/g, ''');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Check if position is at line start (only whitespace before it)
|
|
69
|
+
*/
|
|
70
|
+
function isLineStart(code, pos) {
|
|
71
|
+
if (pos === 0) return true;
|
|
72
|
+
for (let i = pos - 1; i >= 0; i--) {
|
|
73
|
+
const ch = code[i];
|
|
74
|
+
if (ch === '\n') return true;
|
|
75
|
+
if (ch !== ' ' && ch !== '\t') return false;
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Highlight C source code
|
|
82
|
+
*/
|
|
83
|
+
function highlightC(code) {
|
|
84
|
+
const tokens = [];
|
|
85
|
+
let i = 0;
|
|
86
|
+
const len = code.length;
|
|
87
|
+
|
|
88
|
+
while (i < len) {
|
|
89
|
+
const char = code[i];
|
|
90
|
+
|
|
91
|
+
// Multi-line comment
|
|
92
|
+
if (char === '/' && code[i + 1] === '*') {
|
|
93
|
+
let end = code.indexOf('*/', i + 2);
|
|
94
|
+
end = (end === -1) ? len : end + 2;
|
|
95
|
+
const comment = code.substring(i, end);
|
|
96
|
+
tokens.push('<span class="c-comment">', escapeHtml(comment), '</span>');
|
|
97
|
+
i = end;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Single-line comment
|
|
102
|
+
if (char === '/' && code[i + 1] === '/') {
|
|
103
|
+
const end = code.indexOf('\n', i);
|
|
104
|
+
const commentEnd = (end === -1) ? len : end;
|
|
105
|
+
const comment = code.substring(i, commentEnd);
|
|
106
|
+
tokens.push('<span class="c-comment">', escapeHtml(comment), '</span>');
|
|
107
|
+
i = commentEnd;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Preprocessor directive (must be at line start)
|
|
112
|
+
if (char === '#' && isLineStart(code, i)) {
|
|
113
|
+
let end = i + 1;
|
|
114
|
+
while (end < len && code[end] !== '\n') {
|
|
115
|
+
if (code[end] === '\\' && end + 1 < len && code[end + 1] === '\n') {
|
|
116
|
+
end += 2; // Handle line continuation
|
|
117
|
+
} else {
|
|
118
|
+
end++;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const preprocessor = code.substring(i, end);
|
|
122
|
+
tokens.push('<span class="c-preprocessor">', escapeHtml(preprocessor), '</span>');
|
|
123
|
+
i = end;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// String literal
|
|
128
|
+
if (char === '"') {
|
|
129
|
+
let end = i + 1;
|
|
130
|
+
while (end < len && code[end] !== '"') {
|
|
131
|
+
if (code[end] === '\\' && end + 1 < len) {
|
|
132
|
+
end += 2; // Skip escaped character
|
|
133
|
+
} else {
|
|
134
|
+
end++;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (end < len) end++; // Include closing quote
|
|
138
|
+
const string = code.substring(i, end);
|
|
139
|
+
tokens.push('<span class="c-string">', escapeHtml(string), '</span>');
|
|
140
|
+
i = end;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Character literal
|
|
145
|
+
if (char === "'") {
|
|
146
|
+
let end = i + 1;
|
|
147
|
+
// Handle escape sequences like '\n', '\\', '\''
|
|
148
|
+
if (end < len && code[end] === '\\' && end + 1 < len) {
|
|
149
|
+
end += 2; // Skip backslash and escaped char
|
|
150
|
+
} else if (end < len) {
|
|
151
|
+
end++; // Single character
|
|
152
|
+
}
|
|
153
|
+
if (end < len && code[end] === "'") end++; // Closing quote
|
|
154
|
+
const charLit = code.substring(i, end);
|
|
155
|
+
tokens.push('<span class="c-value">', escapeHtml(charLit), '</span>');
|
|
156
|
+
i = end;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Number (integer or float)
|
|
161
|
+
if (char >= '0' && char <= '9') {
|
|
162
|
+
let end = i;
|
|
163
|
+
|
|
164
|
+
// Hexadecimal
|
|
165
|
+
if (char === '0' && (code[i + 1] === 'x' || code[i + 1] === 'X')) {
|
|
166
|
+
end = i + 2;
|
|
167
|
+
while (end < len) {
|
|
168
|
+
const ch = code[end];
|
|
169
|
+
if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) {
|
|
170
|
+
end++;
|
|
171
|
+
} else {
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Octal
|
|
177
|
+
else if (char === '0' && code[i + 1] >= '0' && code[i + 1] <= '7') {
|
|
178
|
+
end = i + 1;
|
|
179
|
+
while (end < len && code[end] >= '0' && code[end] <= '7') end++;
|
|
180
|
+
}
|
|
181
|
+
// Decimal/Float
|
|
182
|
+
else {
|
|
183
|
+
while (end < len) {
|
|
184
|
+
const ch = code[end];
|
|
185
|
+
if ((ch >= '0' && ch <= '9') || ch === '.') {
|
|
186
|
+
end++;
|
|
187
|
+
} else {
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Scientific notation
|
|
192
|
+
if (end < len && (code[end] === 'e' || code[end] === 'E')) {
|
|
193
|
+
end++;
|
|
194
|
+
if (end < len && (code[end] === '+' || code[end] === '-')) end++;
|
|
195
|
+
while (end < len && code[end] >= '0' && code[end] <= '9') end++;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Suffix (u, l, f, etc.)
|
|
200
|
+
while (end < len) {
|
|
201
|
+
const ch = code[end];
|
|
202
|
+
if (ch === 'u' || ch === 'U' || ch === 'l' || ch === 'L' || ch === 'f' || ch === 'F') {
|
|
203
|
+
end++;
|
|
204
|
+
} else {
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const number = code.substring(i, end);
|
|
210
|
+
tokens.push('<span class="c-value">', escapeHtml(number), '</span>');
|
|
211
|
+
i = end;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Identifier or keyword
|
|
216
|
+
if ((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || char === '_') {
|
|
217
|
+
let end = i + 1;
|
|
218
|
+
while (end < len) {
|
|
219
|
+
const ch = code[end];
|
|
220
|
+
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
|
|
221
|
+
(ch >= '0' && ch <= '9') || ch === '_') {
|
|
222
|
+
end++;
|
|
223
|
+
} else {
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const word = code.substring(i, end);
|
|
228
|
+
|
|
229
|
+
if (C_KEYWORDS.has(word)) {
|
|
230
|
+
tokens.push('<span class="c-keyword">', escapeHtml(word), '</span>');
|
|
231
|
+
} else if (isType(word)) {
|
|
232
|
+
// Check types before macros (VALUE, ID are types, not macros)
|
|
233
|
+
tokens.push('<span class="c-type">', escapeHtml(word), '</span>');
|
|
234
|
+
} else if (isMacro(word)) {
|
|
235
|
+
tokens.push('<span class="c-macro">', escapeHtml(word), '</span>');
|
|
236
|
+
} else {
|
|
237
|
+
// Check if followed by '(' -> function name
|
|
238
|
+
let nextCharIdx = end;
|
|
239
|
+
while (nextCharIdx < len && (code[nextCharIdx] === ' ' || code[nextCharIdx] === '\t')) {
|
|
240
|
+
nextCharIdx++;
|
|
241
|
+
}
|
|
242
|
+
if (nextCharIdx < len && code[nextCharIdx] === '(') {
|
|
243
|
+
tokens.push('<span class="c-function">', escapeHtml(word), '</span>');
|
|
244
|
+
} else {
|
|
245
|
+
tokens.push('<span class="c-identifier">', escapeHtml(word), '</span>');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
i = end;
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Operators
|
|
253
|
+
if (OPERATOR_CHARS.has(char)) {
|
|
254
|
+
let op = char;
|
|
255
|
+
// Check for two-character operators
|
|
256
|
+
if (i + 1 < len) {
|
|
257
|
+
const twoChar = char + code[i + 1];
|
|
258
|
+
if (OPERATORS.has(twoChar)) {
|
|
259
|
+
op = twoChar;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
tokens.push('<span class="c-operator">', escapeHtml(op), '</span>');
|
|
263
|
+
i += op.length;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Everything else (punctuation, whitespace)
|
|
268
|
+
tokens.push(escapeHtml(char));
|
|
269
|
+
i++;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return tokens.join('');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Initialize C syntax highlighting on page load
|
|
277
|
+
*/
|
|
278
|
+
function initHighlighting() {
|
|
279
|
+
const codeBlocks = document.querySelectorAll('pre[data-language="c"]');
|
|
280
|
+
|
|
281
|
+
codeBlocks.forEach(block => {
|
|
282
|
+
if (block.getAttribute('data-highlighted') === 'true') {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const code = block.textContent;
|
|
287
|
+
const highlighted = highlightC(code);
|
|
288
|
+
|
|
289
|
+
block.innerHTML = highlighted;
|
|
290
|
+
block.setAttribute('data-highlighted', 'true');
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (document.readyState === 'loading') {
|
|
295
|
+
document.addEventListener('DOMContentLoaded', initHighlighting);
|
|
296
|
+
} else {
|
|
297
|
+
initHighlighting();
|
|
298
|
+
}
|
|
299
|
+
})();
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
SearchController = function(data, input, result) {
|
|
2
|
+
this.data = data;
|
|
3
|
+
this.input = input;
|
|
4
|
+
this.result = result;
|
|
5
|
+
|
|
6
|
+
this.current = null;
|
|
7
|
+
this.view = this.result.parentNode;
|
|
8
|
+
this.ranker = new SearchRanker(data.index);
|
|
9
|
+
this.init();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
SearchController.prototype = Object.assign({}, SearchNavigation, new function() {
|
|
13
|
+
var suid = 1;
|
|
14
|
+
|
|
15
|
+
this.init = function() {
|
|
16
|
+
var _this = this;
|
|
17
|
+
var observer = function(e) {
|
|
18
|
+
switch(e.key) {
|
|
19
|
+
case 'ArrowUp':
|
|
20
|
+
case 'ArrowDown':
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
_this.search(_this.input.value);
|
|
24
|
+
};
|
|
25
|
+
this.input.addEventListener('keyup', observer);
|
|
26
|
+
this.input.addEventListener('click', observer); // mac's clear field
|
|
27
|
+
|
|
28
|
+
this.ranker.ready(function(results, isLast) {
|
|
29
|
+
_this.addResults(results, isLast);
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
this.initNavigation();
|
|
33
|
+
this.setNavigationActive(false);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.search = function(value, selectFirstMatch) {
|
|
37
|
+
this.selectFirstMatch = selectFirstMatch;
|
|
38
|
+
|
|
39
|
+
value = value.trim();
|
|
40
|
+
if (value) {
|
|
41
|
+
this.setNavigationActive(true);
|
|
42
|
+
} else {
|
|
43
|
+
this.setNavigationActive(false);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (value == '') {
|
|
47
|
+
this.lastQuery = value;
|
|
48
|
+
this.result.innerHTML = '';
|
|
49
|
+
this.result.setAttribute('aria-expanded', 'false');
|
|
50
|
+
this.setNavigationActive(false);
|
|
51
|
+
} else if (value != this.lastQuery) {
|
|
52
|
+
this.lastQuery = value;
|
|
53
|
+
this.result.setAttribute('aria-busy', 'true');
|
|
54
|
+
this.result.setAttribute('aria-expanded', 'true');
|
|
55
|
+
this.firstRun = true;
|
|
56
|
+
this.ranker.find(value);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.addResults = function(results, isLast) {
|
|
61
|
+
var target = this.result;
|
|
62
|
+
if (this.firstRun && (results.length > 0 || isLast)) {
|
|
63
|
+
this.current = null;
|
|
64
|
+
this.result.innerHTML = '';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (var i=0, l = results.length; i < l; i++) {
|
|
68
|
+
var item = this.renderItem.call(this, results[i]);
|
|
69
|
+
item.setAttribute('id', 'search-result-' + target.childElementCount);
|
|
70
|
+
target.appendChild(item);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
if (this.firstRun && results.length > 0) {
|
|
74
|
+
this.firstRun = false;
|
|
75
|
+
this.current = target.firstChild;
|
|
76
|
+
this.current.classList.add('search-selected');
|
|
77
|
+
}
|
|
78
|
+
//TODO: ECMAScript
|
|
79
|
+
//if (jQuery.browser.msie) this.$element[0].className += '';
|
|
80
|
+
|
|
81
|
+
if (this.selectFirstMatch && this.current) {
|
|
82
|
+
this.selectFirstMatch = false;
|
|
83
|
+
this.select(this.current);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (isLast) {
|
|
87
|
+
this.selectFirstMatch = false;
|
|
88
|
+
this.result.setAttribute('aria-busy', 'false');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this.move = function(isDown) {
|
|
93
|
+
if (!this.current) return;
|
|
94
|
+
var next = isDown ? this.current.nextElementSibling : this.current.previousElementSibling;
|
|
95
|
+
if (next) {
|
|
96
|
+
this.current.classList.remove('search-selected');
|
|
97
|
+
next.classList.add('search-selected');
|
|
98
|
+
this.input.setAttribute('aria-activedescendant', next.getAttribute('id'));
|
|
99
|
+
this.scrollIntoView(next, this.view);
|
|
100
|
+
this.current = next;
|
|
101
|
+
this.input.value = next.firstChild.firstChild.text;
|
|
102
|
+
this.input.select();
|
|
103
|
+
}
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.hlt = function(html) {
|
|
108
|
+
return this.escapeHTML(html).
|
|
109
|
+
replace(/\u0001/g, '<em>').
|
|
110
|
+
replace(/\u0002/g, '</em>');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this.escapeHTML = function(html) {
|
|
114
|
+
return html.replace(/[&<>"`']/g, function(c) {
|
|
115
|
+
return '&#' + c.charCodeAt(0) + ';';
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
});
|
|
120
|
+
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SearchNavigation allows movement using the arrow keys through the search results.
|
|
3
|
+
*
|
|
4
|
+
* When using this library you will need to set scrollIntoView to the
|
|
5
|
+
* appropriate function for your layout. Use scrollInWindow if the container
|
|
6
|
+
* is not scrollable and scrollInElement if the container is a separate
|
|
7
|
+
* scrolling region.
|
|
8
|
+
*/
|
|
9
|
+
SearchNavigation = new function() {
|
|
10
|
+
this.initNavigation = function() {
|
|
11
|
+
var _this = this;
|
|
12
|
+
|
|
13
|
+
document.addEventListener('keydown', function(e) {
|
|
14
|
+
_this.onkeydown(e);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
this.navigationActive = true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
this.setNavigationActive = function(state) {
|
|
21
|
+
this.navigationActive = state;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.onkeydown = function(e) {
|
|
25
|
+
if (!this.navigationActive) return;
|
|
26
|
+
switch(e.key) {
|
|
27
|
+
case 'ArrowLeft':
|
|
28
|
+
if (this.moveLeft()) e.preventDefault();
|
|
29
|
+
break;
|
|
30
|
+
case 'ArrowUp':
|
|
31
|
+
if (e.key == 'ArrowUp' || e.ctrlKey) {
|
|
32
|
+
if (this.moveUp()) e.preventDefault();
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
case 'ArrowRight':
|
|
36
|
+
if (this.moveRight()) e.preventDefault();
|
|
37
|
+
break;
|
|
38
|
+
case 'ArrowDown':
|
|
39
|
+
if (e.key == 'ArrowDown' || e.ctrlKey) {
|
|
40
|
+
if (this.moveDown()) e.preventDefault();
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
case 'Enter':
|
|
44
|
+
if (this.current) e.preventDefault();
|
|
45
|
+
this.select(this.current);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
if (e.ctrlKey && e.shiftKey) this.select(this.current);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this.moveRight = function() {
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.moveLeft = function() {
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.move = function(isDown) {
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.moveUp = function() {
|
|
61
|
+
return this.move(false);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.moveDown = function() {
|
|
65
|
+
return this.move(true);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/*
|
|
69
|
+
* Scrolls to the given element in the scrollable element view.
|
|
70
|
+
*/
|
|
71
|
+
this.scrollInElement = function(element, view) {
|
|
72
|
+
var offset, viewHeight, viewScroll, height;
|
|
73
|
+
offset = element.offsetTop;
|
|
74
|
+
height = element.offsetHeight;
|
|
75
|
+
viewHeight = view.offsetHeight;
|
|
76
|
+
viewScroll = view.scrollTop;
|
|
77
|
+
|
|
78
|
+
if (offset - viewScroll + height > viewHeight) {
|
|
79
|
+
view.scrollTop = offset - viewHeight + height;
|
|
80
|
+
}
|
|
81
|
+
if (offset < viewScroll) {
|
|
82
|
+
view.scrollTop = offset;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/*
|
|
87
|
+
* Scrolls to the given element in the window. The second argument is
|
|
88
|
+
* ignored
|
|
89
|
+
*/
|
|
90
|
+
this.scrollInWindow = function(element, ignored) {
|
|
91
|
+
var offset, viewHeight, viewScroll, height;
|
|
92
|
+
offset = element.offsetTop;
|
|
93
|
+
height = element.offsetHeight;
|
|
94
|
+
viewHeight = window.innerHeight;
|
|
95
|
+
viewScroll = window.scrollY;
|
|
96
|
+
|
|
97
|
+
if (offset - viewScroll + height > viewHeight) {
|
|
98
|
+
window.scrollTo(window.scrollX, offset - viewHeight + height);
|
|
99
|
+
}
|
|
100
|
+
if (offset < viewScroll) {
|
|
101
|
+
window.scrollTo(window.scrollX, offset);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|