diff --git a/collects/scribble/html-render.ss b/collects/scribble/html-render.ss
index 4e1ebf84b0..e96e87c625 100644
--- a/collects/scribble/html-render.ss
+++ b/collects/scribble/html-render.ss
@@ -721,11 +721,15 @@
`((span ([title ,(hover-element-text e)])
,@(render-plain-element e part ri)))]
[(script-element? e)
- `((script ([type ,(script-element-type e)])
- ,(apply literal `("\n" ,@(script-element-script e) "\n")))
- ;; mynoscript hack doesn't always work (see hack in scribble-common.js)
- (noscript ,@(render-plain-element e part ri))
- )]
+ (let* ([t `[type ,(script-element-type e)]]
+ [s (script-element-script e)]
+ [s (if (list? s)
+ `(script (,t) ,(apply literal `("\n" ,@s "\n")))
+ `(script (,t [src ,s])))])
+ (list s
+ ;; mynoscript hack doesn't always work (see the
+ ;; (commented) hack in scribble-common.js)
+ `(noscript ,@(render-plain-element e part ri))))]
[(target-element? e)
`((a ([name ,(format "~a" (anchor-name (tag-key (target-element-tag e)
ri)))]))
diff --git a/collects/scribble/struct.ss b/collects/scribble/struct.ss
index 2ef0942f9f..dcad36256e 100644
--- a/collects/scribble/struct.ss
+++ b/collects/scribble/struct.ss
@@ -166,7 +166,7 @@
[(aux-element element) ()]
[(hover-element element) ([text string?])]
[(script-element element) ([type string?]
- [script (listof string?)])]
+ [script (or/c path-string? (listof string?))])]
;; specific renders support other elements, especially strings
[with-attributes ([style any/c]
@@ -176,7 +176,7 @@
[parent (or/c false/c part?)]
[info any/c])]
- [target-url ([addr (or/c string? path?)] [style any/c])]
+ [target-url ([addr path-string?] [style any/c])]
[url-anchor ([name string?])]
[image-file ([path (or/c path-string?
(cons/c (one-of/c 'collects)
diff --git a/collects/scribblings/main/private/make-search.ss b/collects/scribblings/main/private/make-search.ss
index 211953f4ce..4e02559ab1 100644
--- a/collects/scribblings/main/private/make-search.ss
+++ b/collects/scribblings/main/private/make-search.ss
@@ -13,11 +13,16 @@
(only-in scheme/class send)
(only-in xml xexpr->string)
(only-in setup/dirs find-doc-dir)
- "utils.ss")
+ "utils.ss"
+ scheme/runtime-path
+ (for-syntax (only-in scheme/base #%datum)))
(provide make-search)
+(define-runtime-path search-script "search.js")
+
(define (make-script user-dir? renderer sec ri)
+ (define dest-dir (send renderer get-dest-directory))
(define span-classes null)
;; To make the index smaller, html contents is represented as one of these:
;; - a string
@@ -118,547 +123,48 @@
;; Note: using ~s to have javascript-quoted strings
(format "[~s,~s,~a,~a]" text href html from-libs)))
- @script[#:noscript @list{Sorry, you must have JavaScript to use this page.}]{
- // the url of the main doc tree, for compact url
- // representation (see also the UncompactUrl function)
- plt_main_url = @(format "~s" main-url);
- // classes to be used for compact representation of html strings in
- // plt_search_data below (see also the UncompactHtml function)
- plt_span_classes = [
- @(add-between (map (lambda (x) (format "~s" (car x)))
- (reverse span-classes))
- ",\n ")];
- // this array has an entry for each index link: [text, url, html, from_lib]
- // - text is a string holding the indexed text
- // - url holds the link (">" prefix means relative to plt_main_url)
- // - html holds either a string, or [idx, html] where idx is an
- // index into plt_span_classes (note: this is recursive)
- // - from_lib is an array of module names for bound identifiers,
- // or the string "module" for a module entry
- plt_search_data = [
- @(add-between l ",\n")];
- // array of pointers to the previous array, for items that are manuals
- plt_manual_ptrs = {
- @(let* ([ms (hash-map manual-refs cons)]
- [ms (sort ms < #:key cdr)]
- [ms (map (lambda (x) (format "~s: ~a" (car x) (cdr x))) ms)])
- (add-between ms ",\n "))};
+ (with-output-to-file (build-path dest-dir "plt-index.js") #:exists 'truncate
+ (lambda ()
+ (for-each
+ display
+ @`{// the url of the main doc tree, for compact url
+ // representation (see also the UncompactUrl function)
+ plt_main_url = @,(format "~s" main-url);@"\n"
+ // classes to be used for compact representation of html strings in
+ // plt_search_data below (see also the UncompactHtml function)
+ plt_span_classes = [
+ @,@(add-between (map (lambda (x) (format "~s" (car x)))
+ (reverse span-classes))
+ ",\n ")];@"\n"
+ // this array has an entry of four items for each index link:
+ // - text is a string holding the indexed text
+ // - url holds the link (">" prefix means relative to plt_main_url)
+ // - html holds either a string, or [idx, html] where idx is an
+ // index into plt_span_classes (note: this is recursive)
+ // - from_lib is an array of module names for bound identifiers,
+ // or the string "module" for a module entry
+ plt_search_data = [
+ @,@(add-between l ",\n")];@"\n"
+ // array of pointers to the previous array, for items that are manuals
+ plt_manual_ptrs = {
+ @,@(let* ([ms (hash-map manual-refs cons)]
+ [ms (sort ms < #:key cdr)]
+ [ms (map (lambda (x) (format "~s: ~a" (car x) (cdr x)))
+ ms)])
+ (add-between ms ",\n "))};
+ })))
- // Globally visible bindings
- var key_handler, toggle_panel, hide_prefs, new_query, refine_query,
- set_show_manuals, set_show_manual_titles, set_results_num,
- set_type_delay, set_highlight_color;
+ (let ([js (build-path dest-dir "search.js")])
+ (when (file-exists? js) (delete-file js))
+ (copy-file search-script js))
- (function(){
-
- // Configuration options (use || in case a cookie exists but is empty)
- 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)) || 300);
- var highlight_color = (GetCookie("PLT_HighlightColor", false) || "#ffd");
- var background_color = "#f8f8f8";
-
- var query, status, 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 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 = 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);
- }
- }
-
- // constants for Compare(() results;
- // `rexact' is for an actual exact match, so we know that we matched
- // *something* and can show exact matches as such, in other words:
- // - < 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
- var C_fail = 0, C_match = 1, C_prefix = 2, C_exact = 3, C_rexact = 4;
-
- function Compare(pat, str) {
- var i = str.indexOf(pat);
- if (i < 0) return C_fail;
- else if (i > 0) return C_match;
- else if (pat.length == str.length) return C_rexact;
- else return C_prefix;
- }
- function MaxCompares(pat, strs) {
- var r = C_fail;
- for (var i=0@";" i)/, "");
- }
-
- 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") return Compare(term,x[0]); // rexact allowed!
- 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: return function(x) {
- switch (Compare(term,x[0])) {
- case C_fail: return C_fail;
- case C_match: case C_prefix: return C_match;
- case C_exact: case C_rexact: return (x[3] ? C_rexact : C_match);
- }
- }
- }
- }
-
- var last_search_term, last_search_term_raw;
- var search_results = [], first_search_result, exact_results_num;
- function DoSearch() {
- var term = query.value;
- if (term == last_search_term_raw) return;
- last_search_term_raw = term;
- term = term.replace(/\s\s*/g," ") // single spaces
- .replace(/^\s/g,"").replace(/\s$/g,""); // trim edge spaces
- if (term == last_search_term) return;
- last_search_term = term;
- status.innerHTML = "Searching " + plt_search_data.length + " entries";
- var terms = (term=="") ? [] : term.split(/ /);
- for (var i=0@";" i= C_rexact) exact_results.push(plt_search_data[i]);
- else if (r > C_fail) search_results.push(plt_search_data[i]);
- }
- exact_results_num = exact_results.length;
- if (exact_results.length > 0)
- search_results = exact_results.concat(search_results);
- }
- first_search_result = 0;
- status.innerHTML = "" + search_results.length + " entries found";
- query.style.backgroundColor =
- ((search_results.length == 0) && (term != "")) ? "#ffe0e0" : "white";
- UpdateResults();
- }
-
- function UncompactUrl(url) {
- return url.replace(/^>/, 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(plt_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)
- status.innerHTML = ((last_search_term=="") ? "" : "No matches found");
- else if (search_results.length <= results_num)
- status.innerHTML = "Showing all matches" + exact;
- else
- status.innerHTML =
- "Showing "
- + (first_search_result+1) + "-"
- + Math.min(first_search_result+results_num,search_results.length)
- + exact
- + " of " + search_results.length
- + ((search_results.length==plt_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";
- }
-
- var search_timer = null;
- function HandleKeyEvent(event) {
- if (search_timer != null) {
- var t = search_timer;
- search_timer = null;
- clearTimeout(t);
- }
- var key = null;
- if (typeof event == "string") key = event;
- else if (event) {
- switch (event.which || event.keyCode) {
- case 13: key = "Enter"; break;
- case 33: key = "PgUp"; break;
- case 34: key = "PgDn"; break;
- }
- }
- switch (key) {
- case "Enter": // enter with no change scrolls
- if (query.value == last_search_term_raw) {
- first_search_result += 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;
- }
- 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 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 panels_shown = { "help": false, "prefs": false };
- function TogglePanel(name) {
- var shown = !panels_shown[name];
- panels_shown[name] = shown;
- if (shown && (name == "prefs")) {
- 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;
- }
- document.getElementById(name + "_panel").style.display =
- shown ? "table-cell" : "none";
- }
- toggle_panel = TogglePanel;
-
- function HidePrefs(event) {
- if ((event.which || event.keyCode) == 27) {
- query.focus();
- panels_shown["prefs"] = true;
- TogglePanel("prefs");
- }
- }
- 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 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;
-
- })();
- })
+ (list
+ (script-ref "plt-index.js"
+ #:noscript @list{Sorry, you must have JavaScript to use this page.})
+ (script-ref "search.js")
+ (make-element (make-with-attributes #f '((id . "plt_search_container")))
+ null)))
(define (make-search user-dir?)
- (make-splice
- (list
- (make-delayed-block
- (lambda (r s i) (make-paragraph (list (make-script user-dir? r s i)))))
- (make-element (make-with-attributes #f '((id . "plt_search_container")))
- null))))
+ (make-delayed-block (lambda (r s i)
+ (make-paragraph (make-script user-dir? r s i)))))
diff --git a/collects/scribblings/main/private/utils.ss b/collects/scribblings/main/private/utils.ss
index 911a7be639..2eac1e2899 100644
--- a/collects/scribblings/main/private/utils.ss
+++ b/collects/scribblings/main/private/utils.ss
@@ -8,7 +8,7 @@
scheme/list
setup/dirs)
-(provide main-page script)
+(provide main-page script script-ref)
(define page-info
(let ([links (filter pair? links)])
@@ -19,6 +19,9 @@
(define (script #:noscript [noscript null] . body)
(make-script-element #f noscript "text/javascript" (flatten body)))
+(define (script-ref #:noscript [noscript null] path)
+ (make-script-element #f noscript "text/javascript" path))
+
;; the second argument specifies installation/user specific, and if
;; it's missing, then it's a page with a single version
(define (main-page id [installation-specific? '?])
diff --git a/collects/scribblings/scribble/struct.scrbl b/collects/scribblings/scribble/struct.scrbl
index edcbdd2316..cd0031f7cd 100644
--- a/collects/scribblings/scribble/struct.scrbl
+++ b/collects/scribblings/scribble/struct.scrbl
@@ -485,11 +485,13 @@ over the element's content.}
@defstruct[(script-element element) ([type string?]
- [script (listof string?)])]{
+ [script (or/c path-string?
+ (listof string?))])]{
For HTML rendering, when scripting is enabled in the browser,
@scheme[script] is used for the element instead of its normal
-content. The @scheme[type] string is normally
+content---it can be either path naming a script file to refer to, or
+the contents of the script. The @scheme[type] string is normally
@scheme["text/javascript"].}
@@ -560,7 +562,7 @@ Computed for each part by the @techlink{collect pass}.
}
-@defstruct[target-url ([addr string?]
+@defstruct[target-url ([addr path-string?]
[style any/c])]{
Used as a style for an @scheme[element]. The @scheme[style] at this