javascript-based search implemented
svn: r10005
This commit is contained in:
parent
46528afa5b
commit
9860c6c7e5
236
collects/scribblings/main/private/make-search.ss
Normal file
236
collects/scribblings/main/private/make-search.ss
Normal file
|
@ -0,0 +1,236 @@
|
|||
#reader scribble/reader
|
||||
#lang scheme/base
|
||||
|
||||
(require scribble/decode
|
||||
scribble/struct
|
||||
;; scribble/manual-struct
|
||||
scheme/list
|
||||
scheme/string
|
||||
scheme/match
|
||||
(only-in scheme/class send)
|
||||
(only-in xml xexpr->string)
|
||||
"utils.ss")
|
||||
|
||||
(provide make-search)
|
||||
|
||||
(define (cadr-string-lists<? a b)
|
||||
(let loop ([a (cadr a)] [b (cadr b)])
|
||||
(cond [(null? b) #f]
|
||||
[(null? a) #t]
|
||||
[(string-ci=? (car a) (car b))
|
||||
(or (loop (cdr a) (cdr b))
|
||||
;; Try string<? so "Foo" still precedes "foo"
|
||||
(string<? (car a) (car b)))]
|
||||
[else (string-ci<? (car a) (car b))])))
|
||||
|
||||
(define (make-script renderer sec ri)
|
||||
(define l null)
|
||||
(hash-for-each
|
||||
(let ([parent (collected-info-parent (part-collected-info sec ri))])
|
||||
(if parent
|
||||
(collected-info-info (part-collected-info parent ri))
|
||||
(collect-info-ext-ht (resolve-info-ci ri))))
|
||||
(lambda (k v)
|
||||
(when (and (pair? k) (eq? 'index-entry (car k)))
|
||||
(set! l (cons (cons (cadr k) v) l)))))
|
||||
(set! l (sort l cadr-string-lists<?))
|
||||
(set! l
|
||||
(for/list ([i l])
|
||||
;; i is (list tag (text ...) (element ...) index-desc)
|
||||
(define-values (tag texts elts desc) (apply values i))
|
||||
(define-values (href html)
|
||||
(let* ([e (add-between elts ", ")]
|
||||
[e (make-link-element "indexlink" e tag)]
|
||||
[e (send renderer render-element e sec ri)])
|
||||
(match e ; should always render to a single `a'
|
||||
[`((a ([href ,href] [class "indexlink"]) . ,body))
|
||||
(values href (string-append* (map xexpr->string body)))]
|
||||
[else (error 'zzz "something bad happened ~s" e)])))
|
||||
;; (and (exported-index-desc? desc)
|
||||
;; (list (exported-index-desc-name desc)
|
||||
;; (exported-index-desc-from-libs desc)))
|
||||
(format "~a[~s, ~s, ~s]"
|
||||
(if (eq? i (car l)) "" ",\n")
|
||||
(string-join texts " ")
|
||||
href
|
||||
html)))
|
||||
@script[#:noscript @list{Sorry, you must have JavaScript to use this page.}]{
|
||||
// this vector has an entry for each index link: [text, url, html]
|
||||
plt_search_data = [
|
||||
@l];
|
||||
|
||||
// Globally visible bindings
|
||||
var search_handler, page_up_handler, page_dn_handler;
|
||||
|
||||
(function(){
|
||||
|
||||
// Configuration options
|
||||
var results_num = 20;
|
||||
|
||||
var query, status, results_container, result_links;
|
||||
|
||||
function InitializeSearch() {
|
||||
var n;
|
||||
n = document.getElementById("plt_search_container").parentNode;
|
||||
// hack the table in
|
||||
n.innerHTML = ''
|
||||
+'<table width="100%">'
|
||||
+'<tr><td align="center" colspan="3">'
|
||||
+'<input type="text" id="search_box" style="width: 100%;"'
|
||||
+'onchange="search_handler(event);"'
|
||||
+'onkeyup="search_handler(event);" />'
|
||||
+'</td></tr>'
|
||||
+'<tr><td align="left">'
|
||||
+'<a href="#" title="Previous Page"'
|
||||
+'onclick="page_up_handler(); return false;"'
|
||||
+'><tt><b><<</b></tt></a>'
|
||||
+'</td><td align="center">'
|
||||
+'<span id="search_status" style="color: #601515; font-weight: bold;">'
|
||||
+' '
|
||||
+'</span>'
|
||||
+'</td><td align="right">'
|
||||
+'<a href="#" title="Next Page"'
|
||||
+'onclick="page_dn_handler(); return false;"'
|
||||
+'><tt><b>>></b></tt></a>'
|
||||
+'</td></tr>'
|
||||
+'<tr><td colspan="3">'
|
||||
+'<span id="search_result"'
|
||||
+'style="display: none; margin: 0.5em 1em;"></span>'
|
||||
+'</td></tr>'
|
||||
+'</table>';
|
||||
// get the query box
|
||||
query = document.getElementById("search_box");
|
||||
// status should point to the text object
|
||||
status = document.getElementById("search_status");
|
||||
if (status.childNodes.length == 1) status = status.firstChild;
|
||||
// result_links is the array of result link <container,link> pairs
|
||||
result_links = new Array();
|
||||
n = document.getElementById("search_result");
|
||||
results_container = n.parentNode;
|
||||
results_container.normalize();
|
||||
result_links.push(n);
|
||||
AdjustResultsNum();
|
||||
// get search string
|
||||
if (location.search.length > 0) {
|
||||
var paramstrs = location.search.substring(1).split(/[@";"&]/);
|
||||
for (var i=0@";" i<paramstrs.length@";" i++) {
|
||||
var param = paramstrs[i].split(/=/);
|
||||
if (param.length == 2 && param[0] == "q") {
|
||||
query.value = unescape(param[1]).replace(/\+/g," ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (query.value != "") DoSearch();
|
||||
query.focus();
|
||||
query.select();
|
||||
}
|
||||
|
||||
function AdjustResultsNum() {
|
||||
if (result_links.length == results_num) return;
|
||||
if (results_num <= 0) results_num = 1; // should have at least one template
|
||||
while (result_links.length > results_num)
|
||||
results_container.removeChild(result_links.pop());
|
||||
while (result_links.length < results_num) {
|
||||
var n = result_links[0].cloneNode(true);
|
||||
result_links.push(n);
|
||||
results_container.appendChild(n);
|
||||
}
|
||||
}
|
||||
|
||||
var last_search_terms;
|
||||
var search_results, first_search_result;
|
||||
function DoSearch() {
|
||||
var terms = query.value.toLowerCase()
|
||||
.replace(/\s\s*/g," ") // single spaces
|
||||
.replace(/^\s/g,"").replace(/\s$/g,""); // trim edge spaces
|
||||
if (terms == last_search_terms) return;
|
||||
last_search_terms = terms;
|
||||
status.nodeValue = "Searching " + plt_search_data.length + " entries";
|
||||
terms = (terms=="") ? [] : terms.split(/ /);
|
||||
if (terms.length == 0) {
|
||||
search_results = plt_search_data;
|
||||
} else {
|
||||
search_results = new Array();
|
||||
for (var i=0@";" i<plt_search_data.length@";" i++) {
|
||||
var show = true, curtext = plt_search_data[i][0];
|
||||
for (var j=0@";" j<terms.length@";" j++) {
|
||||
if (curtext.indexOf(terms[j]) < 0) {
|
||||
show = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (show) search_results.push(plt_search_data[i]);
|
||||
}
|
||||
}
|
||||
first_search_result = 0;
|
||||
status.nodeValue = "" + search_results.length + " entries found";
|
||||
query.style.backgroundColor =
|
||||
(search_results.length == 0) ? "#ffe0e0" : "white";
|
||||
UpdateResults();
|
||||
}
|
||||
|
||||
function PageDn() {
|
||||
first_search_result += results_num@";" UpdateResults()@";"
|
||||
}
|
||||
function PageUp() {
|
||||
first_search_result -= results_num@";" UpdateResults()@";"
|
||||
}
|
||||
|
||||
function UpdateResults() {
|
||||
if (first_search_result < 0 ||
|
||||
first_search_result >= search_results.length)
|
||||
first_search_result = 0;
|
||||
for (var i=0@";" i<result_links.length@";" i++) {
|
||||
var n = i + first_search_result;
|
||||
if (n < search_results.length) {
|
||||
result_links[i].innerHTML =
|
||||
'<a href="'+search_results[n][1]+'" class="indexlink">'
|
||||
+ search_results[n][2] + '</a>';
|
||||
result_links[i].style.display = "block";
|
||||
} else {
|
||||
result_links[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
if (search_results.length == 0)
|
||||
status.nodeValue = "No matches found";
|
||||
else if (search_results.length <= results_num)
|
||||
status.nodeValue = "Showing all " + search_results.length + " matches";
|
||||
else
|
||||
status.nodeValue =
|
||||
"Showing "
|
||||
+ (first_search_result+1) + "--"
|
||||
+ Math.min(first_search_result+results_num,search_results.length)
|
||||
+ " of " + search_results.length + " matches";
|
||||
}
|
||||
|
||||
var search_timer = null;
|
||||
function DelayedSearch(event) {
|
||||
if (search_timer != null) {
|
||||
var t = search_timer;
|
||||
search_timer = null;
|
||||
clearTimeout(t);
|
||||
}
|
||||
var key = event && event.keyCode;
|
||||
if (key == 13) DoSearch();
|
||||
else if (key == 33) PageUp();
|
||||
else if (key == 34) PageDn();
|
||||
else search_timer = setTimeout(DoSearch, 1000);
|
||||
}
|
||||
|
||||
search_handler = DelayedSearch;
|
||||
page_up_handler = PageUp;
|
||||
page_dn_handler = PageDn;
|
||||
|
||||
window.onload = InitializeSearch;
|
||||
|
||||
})();
|
||||
})
|
||||
|
||||
(define (make-search)
|
||||
(make-splice
|
||||
(list
|
||||
(make-delayed-block
|
||||
(lambda (r s i) (make-paragraph (list (make-script r s i)))))
|
||||
(make-element (make-with-attributes #f '((id . "plt_search_container")))
|
||||
null))))
|
|
@ -5,9 +5,10 @@
|
|||
scribble/manual
|
||||
scribble/struct
|
||||
scribble/decode
|
||||
scheme/list
|
||||
setup/dirs)
|
||||
|
||||
(provide main-page)
|
||||
(provide main-page script)
|
||||
|
||||
(define page-info
|
||||
(let ([links (filter pair? links)])
|
||||
|
@ -15,8 +16,8 @@
|
|||
(cond [(assq id links) => cdr]
|
||||
[else (error 'main-page "page id not found: ~e" id)]))))
|
||||
|
||||
(define (script . body)
|
||||
(make-script-element #f null "text/javascript" body))
|
||||
(define (script #:noscript [noscript null] . body)
|
||||
(make-script-element #f noscript "text/javascript" (flatten body)))
|
||||
|
||||
;; the second argument specifies installation/user specific, and if
|
||||
;; it's missing, then it's a page with a single version
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#lang scribble/doc
|
||||
@(require scribble/basic
|
||||
scribble/decode
|
||||
"private/utils.ss")
|
||||
@(require "private/utils.ss"
|
||||
"private/make-search.ss")
|
||||
|
||||
@main-page['search #t]
|
||||
|
||||
Search page not implemented yet.
|
||||
@make-search[]
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#lang scribble/doc
|
||||
@(require scribble/basic
|
||||
scribble/decode
|
||||
"../private/utils.ss")
|
||||
@(require "../private/utils.ss"
|
||||
"../private/make-search.ss")
|
||||
|
||||
@main-page['search #f]
|
||||
|
||||
Search page not implemented yet.
|
||||
@make-search[]
|
||||
|
|
Loading…
Reference in New Issue
Block a user