// Globally visible bindings
var key_handler, toggle_panel, hide_prefs, new_query, refine_query,
set_pre_query, set_show_manuals, set_show_manual_titles, set_results_num,
set_type_delay, set_highlight_color, status_line, saved_status = false;
(function(){
// Configuration options (use || in case a cookie exists but is empty)
var pre_query = GetCookie("PLT_PreQuery","");
var manual_settings = parseInt(GetCookie("PLT_ManualSettings",1));
var show_manuals = manual_settings % 10;
var show_manual_titles = ((manual_settings - show_manuals) / 10) > 0;
var results_num = (parseInt(GetCookie("PLT_ResultsNum",false)) || 20);
var type_delay = (parseInt(GetCookie("PLT_TypeDelay",false)) || 150);
var highlight_color = (GetCookie("PLT_HighlightColor",false) || "#ffd");
var background_color = "#f8f8f8";
var query, results_container, result_links, prev_page_link, next_page_link;
// tabIndex fields are set:
// 1 query
// 2 index links
// 3 help/pref toggle
// 4 pref widgets
// -1 prev/next page (un-tab-able)
function MakePref(label, input) {
return '
' + label + ': | '
+'' + input + ' |
';
}
descriptions = new Array();
function PrefInputArgs(name, desc) {
// don't plant `desc' directly in the text -- it might have quotes
descriptions[name] = desc;
return 'tabIndex="4" id="'+name+'_pref"'
+' onkeypress="hide_prefs(event);"'
+' onchange="set_'+name+'(this); return true;"'
+' onfocus="saved_status=status_line.innerHTML;'
+'status_line.innerHTML=descriptions[\''+name+'\'];"'
+' onblur="status_line.innerHTML = saved_status || \'\';"';
}
function InitializeSearch() {
var n;
n = document.getElementById("plt_search_container").parentNode;
// hack the table in
n.innerHTML = ''
+'';
// get the widgets we use
query = document.getElementById("search_box");
status_line = document.getElementById("search_status");
prev_page_link = document.getElementById("prev_page_link");
next_page_link = document.getElementById("next_page_link");
// result_links is the array of result 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 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);
}
}
// Terms are compared using Compare(), which returns one of several constants:
// - C_fail: there was no match
// - C_match: there was a match somewhere in the string
// - C_prefix: there was a prefix match (starts at 0)
// - C_rexact: there was a ("real") exact match
// There is also C_exact which can be returned by some of the X: operators.
// It's purpose is to be able to return a result that doesn't affect the
// exactness of the search (for example L:foo searches for a source module that
// is exactly `foo', but it will return C_exact which means that it doesn't
// change whether the match is considered exact or not). Note that these
// constants are ordered, so:
// - < exact => this match is inexact
// - = exact => does not affect the exactness of this match
// - > exact => this is an exact match as far as this predicate goes
// Finally, there is also a C_wordmatch that is used when there is no proper
// match, but all the words in the term match (where a word is an alphanumeric
// sequence or a punctuation, for example, "foo-bar!!" has these words: "foo",
// "-", "bar", "!!")
var C_fail = 0, C_min = 0,
C_wordmatch = 1,
C_match = 2,
C_prefix = 3,
C_exact = 4,
C_rexact = 5, C_max = 5;
function Compare(pat, str) {
var i = str.indexOf(pat);
if (i < 0) return C_fail;
if (i > 0) return C_match;
if (pat.length == str.length) return C_rexact;
return C_prefix;
}
function CompareRx(pat, str) {
if (!(pat instanceof RegExp)) return Compare(pat,str);
var r = str.match(pat);
if (r == null || r.length == 0) return C_fail;
if (r[0] == str) return C_rexact;
if (str.indexOf(r[0]) == 0) return C_prefix;
return C_match;
}
function MaxCompares(pat, strs) {
var r = C_min;
for (var i=0; i= C_max) return r;
}
return r;
}
function MinComparesRx(pats, str) {
var r = C_max;
for (var i=0; i)/, ""); // and directory.
}
// Tests for matches and highlights:
// "append"
// "L:scheme append"
// "L:scheme" (no exact matches except for the `scheme' module)
// "L:schem" (only module names that match `schem')
function CompileTerm(term) {
var flag = ((term.search(/^[LMT]:/)==0) && term.substring(0,1));
if (flag) term = term.substring(2);
term = term.toLowerCase();
switch(flag) {
case "L":
return function(x) {
if (!x[3]) return C_fail;
if (x[3] == "module") // rexact allowed, show partial module matches
return Compare(term,x[0]);
return (MaxCompares(term,x[3]) >= C_exact) ? C_exact : C_fail;
}
case "M":
return function(x) {
if (!x[3]) return C_fail;
if (x[3] == "module") return Compare(term,x[0]); // rexact allowed
return (MaxCompares(term,x[3]) >= C_match) ? C_exact : C_fail;
}
case "T":
return function(x) {
if (Compare(term,UrlToManual(x[1])) < C_exact) return C_fail;
else if (x[1].search(/\/index\.html$/) > 0) return C_rexact;
else return C_exact;
}
default:
var words = term.split(/\b/);
for (var i=0; i= 0) words[i] = new RegExp("\\b"+words[i]);
// (note: seems like removing duplicates is not important since search
// strings will not have them, except, maybe, for an occasional x-y-z)
return function(x) {
var r = Compare(term,x[0]);
// only bindings can be used for exact matches
if (r >= C_exact) return (x[3] ? C_rexact : C_match);
if (r >= C_match) return r;
if (MinComparesRx(words,x[0]) > C_fail) return C_wordmatch;
return r;
}
}
}
function Id(x) {
return x;
}
function MakeShowProgress() {
var orig = status_line.innerHTML;
var indicators = [
">.........", "->........", "-->.......", "--->......", "---->.....",
"----->....", "------>...", "------->..", "-------->.", "--------->",
"---------*"];
return function(n) {
status_line.innerHTML =
orig + " "
+ indicators[Math.round(10*n/search_data.length)] + "";
}
}
function Search(data, term, is_pre, K) {
// `K' is a continuation, if this run is supposed to happen in a "thread"
// false otherwise
var t = false;
var killer = function() { if (t) clearTimeout(t); };
// term comes with normalized spaces (trimmed, and no doubles)
var preds = (term=="") ? [] : term.split(/ /);
for (var i=0; i 0) {
var r, min = C_max, max = C_min;
for (var j=0; j= C_rexact && min >= C_exact) exacts.push(data[i]);
else if (min > C_wordmatch) matches.push(data[i]);
else if (min > C_fail) wordmatches.push(data[i]);
fuel--; i++;
}
if (i/, plt_main_url);
}
function UncompactHtml(x) {
if (typeof x == "string") {
return x;
} else if (!(x instanceof Array)) {
alert("Internal error in PLT docs");
} else if ((x.length == 2) && (typeof(x[0]) == "number")) {
return '' + UncompactHtml(x[1]) + '';
} else {
var s = "";
for (var i=0; i= search_results.length)
first_search_result = 0;
for (var i=0; i 0)) {
note = 'provided from ';
for (var j=0; j'
+ desc[j] + '';
} else if (desc == "module") {
note = 'module';
}
if (show_manuals == 2 || (show_manuals == 1 && !desc)) {
var manual = UrlToManual(res[1]),
idx = (show_manual_titles && plt_manual_ptrs[manual]);
note = (note ? (note + " ") : "");
note += 'in '
+ ''
+ ((typeof idx == "number")
? (''+UncompactHtml(search_data[idx][2])+'')
: manual)
+ '';
}
if (note)
note = ' ' + note + '';
result_links[i].innerHTML =
''
+ UncompactHtml(res[2]) + '' + (note || "");
result_links[i].style.backgroundColor =
(n < exact_results_num) ? highlight_color : background_color;
result_links[i].style.display = "block";
} else {
result_links[i].style.display = "none";
}
}
var exact = Math.min((exact_results_num - first_search_result),
results_num);
exact = (exact <= 0) ? ''
: ' ('
+ ((exact == results_num) ? 'all' : exact)
+ ' exact)';
if (search_results.length == 0) {
if (last_search_term == "") status_line.innerHTML = "";
else status_line.innerHTML = 'No matches found '
+ ''
+ '(Make sure your spelling is correct'
+ (last_search_term.search(/ /)>=0 ? ', or try fewer keywords' : '')
+ ')
';
} else if (search_results.length <= results_num)
status_line.innerHTML = "Showing all matches" + exact;
else
status_line.innerHTML =
"Showing "
+ (first_search_result+1) + "-"
+ Math.min(first_search_result+results_num,search_results.length)
+ exact
+ " of " + search_results.length
+ ((search_results.length==search_data.length) ? "" : " matches");
prev_page_link.style.color =
(first_search_result-results_num >= 0) ? "black" : "#e0e0e0";
next_page_link.style.color =
(first_search_result+results_num < search_results.length)
? "black" : "#e0e0e0";
saved_status = false;
}
var search_timer = false;
function HandleKeyEvent(event) {
var key = null;
if (typeof event == "string") key = event;
else if (event) {
switch (event.which || event.keyCode) {
case 13: key = (event.ctrlKey ? "C-Enter" : "Enter"); break;
case 33: key = "PgUp"; break;
case 34: key = "PgDn"; break;
}
}
// note: uses of DoSearch() below starts a background search, which
// means that the operation can still be done on the previously
// displayed results.
switch (key) {
case "Enter": // starts a search immediately
DoSearch();
return false;
case "C-Enter": // C-enter with no change scrolls down (S -> up)
if (query.value == last_search_term_raw) {
if (!event.shiftKey) first_search_result += results_num;
else if (first_search_result > 0) first_search_result -= results_num;
else first_search_result = search_results.length - results_num;
UpdateResults();
} else {
DoSearch();
}
return false;
case "PgUp":
DoSearch(); // in case we didn't update it yet
first_search_result -= results_num;
UpdateResults();
return false;
case "PgDn":
DoSearch(); // in case we didn't update it yet
if (first_search_result + results_num < search_results.length) {
first_search_result += results_num;
UpdateResults();
}
return false;
}
// what if we get a key just before the results are shown? we might
// get them displayed and then the new search begins. this can be
// solved by using kill_bg_search() here, but then *all* keys abort
// the current search, and that's a problem in case the new key
// won't make DoSearch start a new search (like Enter or using Tab).
// so leave it without that.
if (search_timer) clearTimeout(search_timer);
search_timer = setTimeout(DoSearch, type_delay);
return true;
}
key_handler = HandleKeyEvent;
// use this one to set the query field without jumping to the current
// url again, since some browsers will reload the whole page for that
// (it would be nice if there was a way to add it to the history too)
function NewQuery(node) {
var m = node.href.search(/[?]q=[^?&;]+$/);
if (m < 0) return true;
else {
query.value = decodeURIComponent(node.href.substring(m+3));
query.focus();
DoSearch();
return false;
}
}
new_query = NewQuery;
// and this appends the query to the current value (it's hooked on the
// oncontextmenu handler that doesn't work everywhere, but at least in
// FF and IE)
function RefineQuery(node) {
var m = node.href.search(/[?]q=[^?&;]+$/);
if (m < 0) return true;
m = decodeURIComponent(node.href.substring(m+3));
if (query.value.indexOf(m) >= 0) return true;
else {
query.value = m + " " + query.value;
query.focus();
DoSearch();
return false;
}
}
refine_query = RefineQuery;
var panel_shown = false;
function TogglePanel(name) {
if (panel_shown)
document.getElementById(panel_shown+"_panel").style.display = "none";
panel_shown = ((panel_shown != name) && name);
if (panel_shown == "prefs") {
document.getElementById("pre_query_pref").value = pre_query;
document.getElementById("show_manuals_pref").selectedIndex = show_manuals;
document.getElementById("show_manual_titles_pref").checked
= show_manual_titles;
document.getElementById("results_num_pref").value = results_num;
document.getElementById("type_delay_pref").value = type_delay;
document.getElementById("highlight_color_pref").value = highlight_color;
}
if (panel_shown)
document.getElementById(panel_shown+"_panel").style.display = "block";
}
toggle_panel = TogglePanel;
function HidePrefs(event) {
if ((event.which || event.keyCode) == 27) {
query.focus();
TogglePanel("prefs"); // this function is called only when it's shown
}
}
hide_prefs = HidePrefs;
function SetShowManuals(inp) {
if (inp.selectedIndex != show_manuals) {
show_manuals = inp.selectedIndex;
SetCookie("PLT_ManualSettings", show_manuals+(show_manual_titles?10:0));
UpdateResults();
}
}
set_show_manuals = SetShowManuals;
function SetPreQuery(inp) {
if (inp.value != pre_query) {
pre_query = inp.value;
SetCookie("PLT_PreQuery", pre_query);
PreFilter();
DoSearch();
}
}
set_pre_query = SetPreQuery;
function SetShowManuals(inp) {
if (inp.selectedIndex != show_manuals) {
show_manuals = inp.selectedIndex;
SetCookie("PLT_ManualSettings", show_manuals+(show_manual_titles?10:0));
UpdateResults();
}
}
set_show_manuals = SetShowManuals;
function SetShowManualTitles(inp) {
if (inp.checked != show_manual_titles) {
show_manual_titles = inp.checked;
SetCookie("PLT_ManualSettings", show_manuals+(show_manual_titles?10:0));
UpdateResults();
}
}
set_show_manual_titles = SetShowManualTitles;
function SetResultsNum(inp) {
var n = (parseInt(inp.value.replace(/[^0-9]+/g,"")) || results_num);
inp.value = n;
if (n != results_num) {
results_num = n;
SetCookie("PLT_ResultsNum", results_num);
AdjustResultsNum();
UpdateResults();
}
}
set_results_num = SetResultsNum;
function SetTypeDelay(inp) {
var n = (parseInt(inp.value.replace(/[^0-9]+/g,"")) || type_delay);
inp.value = n;
if (n != type_delay) {
type_delay = n;
SetCookie("PLT_TypeDelay", type_delay);
}
}
set_type_delay = SetTypeDelay;
function SetHighlightColor(inp) {
var c = (inp.value.replace(/[^a-zA-Z0-9#]/g,"") || highlight_color);
inp.value = c;
if (c != highlight_color) {
highlight_color = c;
SetCookie("PLT_HighlightColor", highlight_color);
UpdateResults();
}
}
set_highlight_color = SetHighlightColor;
window.onload = InitializeSearch;
})();