summaryrefslogtreecommitdiffstats
path: root/quicksearch.js
diff options
context:
space:
mode:
authorcyeh%bluemartini.com <>2001-03-01 08:07:24 +0100
committercyeh%bluemartini.com <>2001-03-01 08:07:24 +0100
commit5b2994b0ce4cf27709b3742a2b63a680464662a0 (patch)
treeac2b46dde7b3a19475df1a8400e99bc3c120e283 /quicksearch.js
parentd0605ef9490b55c38d090c21b5993eda143e7e86 (diff)
downloadbugzilla-5b2994b0ce4cf27709b3742a2b63a680464662a0.tar.gz
bugzilla-5b2994b0ce4cf27709b3742a2b63a680464662a0.tar.xz
fix for 69793: check in new files for QuickSearch
patch contributed by franke@ags.uni-sb.de (Andreas Franke) Now add quicksearch functionality to the main index page.
Diffstat (limited to 'quicksearch.js')
-rw-r--r--quicksearch.js670
1 files changed, 670 insertions, 0 deletions
diff --git a/quicksearch.js b/quicksearch.js
new file mode 100644
index 000000000..e14710c9a
--- /dev/null
+++ b/quicksearch.js
@@ -0,0 +1,670 @@
+//
+// This is the main JS file for QuickSearch.
+//
+// Derived from:
+//
+// * C. Begle's SimpleSearch tool:
+// http://www.mozilla.org/quality/help/simplesearch.html
+// http://www.mozilla.org/quality/help/bugreport.js
+//
+// * Jesse Ruderman's bugzilla search page:
+// http://www.cs.hmc.edu/~jruderma/s/bugz.html
+//
+// Created by
+// Andreas Franke <afranke@mathweb.org>
+
+function go_to (url) {
+ document.location.href = url;
+ //window.open(url, "other" );
+}
+
+function map(l, f) {
+ l1 = new Array();
+ for (var i=0; i<l.length; i++) {
+ l1[i] = f(l[i]);
+ }
+ return l1;
+}
+
+function isPrefix(s1, s2) {
+ return (s1.length <= s2.length) &&
+ (s1 == s2.substring(0,s1.length))
+}
+
+function member(s, l) {
+ return (l.length > 0)
+ && ((s == l[0]) || member(s, l.slice(1)));
+}
+
+function add(s, l) {
+ if (! member(s, l)) {
+ l.unshift(s);
+ }
+}
+
+function addAll(l1, l2) {
+ for (var i=0; i<l1.length; i++) {
+ add(l1[i],l2);
+ }
+}
+
+function isSubset (l1, l2) {
+ return (l1.length == 0)
+ || (member(l1[0],l2) && subset(l1.slice(1),l2));
+}
+
+// fields
+
+var f1 = new Array();
+var f2 = new Array();
+
+function add_mapping(from,to) {
+ f1[f1.length] = from;
+ f2[f2.length] = to;
+}
+
+// Status, Resolution, Platform, OS, Priority, Severity
+add_mapping("status", "bug_status");
+add_mapping("resolution", "resolution"); // no change
+add_mapping("platform", "rep_platform");
+add_mapping("os", "op_sys");
+add_mapping("opsys", "op_sys");
+add_mapping("priority", "priority"); // no change
+add_mapping("pri", "priority");
+add_mapping("severity", "bug_severity");
+add_mapping("sev", "bug_severity");
+// People: AssignedTo, Reporter, QA Contact, CC, Added comment (?)
+add_mapping("owner", "assigned_to");
+add_mapping("assignee", "assigned_to");
+add_mapping("assignedto", "assigned_to");
+add_mapping("reporter", "reporter"); // no change
+add_mapping("rep", "reporter");
+add_mapping("qa", "qa_contact");
+add_mapping("qacontact", "qa_contact");
+add_mapping("cc", "cc"); // no change
+// Product, Version, Component, Target Milestone
+add_mapping("product", "product"); // no change
+add_mapping("prod", "product");
+add_mapping("version", "version"); // no change
+add_mapping("ver", "version");
+add_mapping("component", "component"); // no change
+add_mapping("comp", "component");
+add_mapping("milestone", "target_milestone");
+add_mapping("target", "target_milestone");
+add_mapping("targetmilestone", "target_milestone");
+// Summary, Description, URL, Status whiteboard, Keywords
+add_mapping("summary", "short_desc");
+add_mapping("shortdesc", "short_desc");
+add_mapping("desc", "longdesc");
+add_mapping("description", "longdesc");
+//add_mapping("comment", "longdesc"); // ???
+ // reserve "comment" for "added comment" email search?
+add_mapping("longdesc", "longdesc");
+add_mapping("url", "bug_file_loc");
+add_mapping("whiteboard", "status_whiteboard");
+add_mapping("statuswhiteboard", "status_whiteboard");
+add_mapping("sw", "status_whiteboard");
+add_mapping("keywords", "keywords"); // no change
+add_mapping("kw", "keywords");
+// Attachments
+add_mapping("attachment", "attachments.description");
+add_mapping("attachmentdesc", "attachments.description");
+add_mapping("attachdesc", "attachments.description");
+add_mapping("attachmentdata", "attachments.thedata");
+add_mapping("attachdata", "attachments.thedata");
+add_mapping("attachmentmimetype", "attachments.mimetype");
+add_mapping("attachmimetype", "attachments.mimetype");
+
+// disabled because of bug 30823:
+// "BugsThisDependsOn" --> "dependson"
+// "OtherBugsDependingOnThis"--> "blocked"
+//add_mapping("dependson", "dependson");
+//add_mapping("blocked", "blocked");
+
+// Substring search doesn't make much sense for the following fields:
+// "Attachment is patch" --> "attachments.ispatch"
+// "Last changed date" --> "delta_ts"
+// "Days since bug changed" --> "(to_days(now()) - to_days(bugs.delta_ts))"
+//"groupset"
+//"everconfirmed"
+//"bug","bugid","bugno" --> "bug_id"
+// "votes" --> "votes"
+// "votes>5", "votes>=5", "votes=>5" works now, see below
+// "votes:5" is interpreted as "votes>=5"
+
+function findIndex(array,value) {
+ for (var i=0; i<array.length; i++)
+ if (array[i] == value) return i;
+ return -1;
+}
+
+function mapField(fieldname) {
+ var i = findIndex(f1,fieldname);
+ if (i >= 0) return f2[i];
+ return undefined;
+}
+
+// `keywords' is defined externally
+
+function is_keyword(s) {
+ return member(s, keywords);
+}
+
+// `platforms' is defined externally
+
+function is_platform(str) {
+ return member (str.toLowerCase(),platforms);
+}
+
+// `severities' is defined externally
+
+function is_severity(str) {
+ return member(str.toLowerCase(),severities);
+}
+
+// `product_exceptions' is defined externally
+
+function match_product(str) {
+ s = str.toLowerCase();
+ return (s.length > 2) && (! member(s,product_exceptions));
+}
+
+// `component_exceptions are defined externally
+
+function match_component(str) {
+ s = str.toLowerCase();
+ return (s.length > 2) && (! member(s,component_exceptions));
+}
+
+var status_and_resolution = ""; // for pretty debug output only; these vars
+var charts = ""; // always hold the data from the last query
+
+// derived from http://www.mozilla.org/quality/help/bugreport.js
+
+function make_chart(expr, field, type, value) {
+ charts += "<tr>" +
+ "<td><tt>" + expr + "</tt></td>" +
+ "<td><tt>" + field + "</tt></td>" +
+ "<td><tt>" + type + "</tt></td>" +
+ "<td><tt>" + value + "</tt></td>" +
+ "</tr>";
+ return "&field" + expr + "=" + field +
+ "&type" + expr + "=" + type +
+ "&value" + expr + "=" + escape(value).replace(/[+]/g,"%2B");
+}
+
+// returns true if at least one of comparelist had the prefix, false otherwise
+function addPrefixMatches(prefix, comparelist, resultlist) {
+ var foundMatch = false;
+ for (var i=0; i<comparelist.length; i++) {
+ if (isPrefix(prefix,comparelist[i])) {
+ foundMatch = true;
+ add(comparelist[i],resultlist);
+ }
+ }
+ return foundMatch;
+}
+
+function prefixesNotFoundError(prefixes,statusValues,resolutionValues) {
+ var txt;
+ if (prefixes.length == 1) {
+ txt = "is not a prefix ";
+ } else {
+ txt = "are not prefixes ";
+ }
+ alert(prefixes + "\n" + txt +
+ "of one of these status or resolution values:\n" +
+ statusValues + "\n" + resolutionValues + "\n");
+}
+
+function make_query_URL(url, input, searchLong) {
+
+ status_and_resolution = "";
+ charts = "";
+
+ var searchURL = url; //bugzilla + "buglist.cgi";
+ var abort = false;
+
+ // escape everything between quotes: "foo bar" --> "foo%20bar"
+ var parts = input.split('"');
+ if ((parts.length % 2) != 1) {
+ alert('Unterminated quote');
+ abort = true;
+ return undefined;
+ }
+ for (var i=1; i<parts.length; i+=2) {
+ parts[i] = escape(parts[i]);
+ }
+ var input2 = parts.join('"');
+
+ // abort if there are still brackets
+ if (input2.match(/[(]|[\)]/)) {
+ alert('Brackets (...) are not supported.\n' +
+ 'Use quotes "..." for values that contain special characters.');
+ abort = true;
+ return undefined;
+ }
+
+ // translate " AND "," OR "," NOT " to space,comma,dash
+ input2 = input2.replace(/[\s]+AND[\s]+/g," ");
+ input2 = input2.replace(/[\s]+OR[\s]+/g,"|");
+ input2 = input2.replace(/[\s]+NOT[\s]+/g," -");
+
+ // now split into words at space positions
+ var word = input2.split(/[\s]+/);
+
+ // determine bug_status and resolution
+ // the first word may contain relevant info
+
+ // `statuses_open' and `statuses_resolved' are defined externally
+ var statusOpen = statuses_open;
+ var statusResolved = statuses_resolved;
+ var statusAll = statusOpen.concat(statusResolved);
+
+ // `resolutions' is defined externally
+ var bug_status = statusOpen.slice().reverse(); //reverse is just cosmetic
+ var resolution = new Array();
+
+ // This function matches the given prefixes against the given statuses and
+ // resolutions. Matched statuses are added to bug_status, matched
+ // resolutions are added to resolution. Returns true iff some matches
+ // were found for at least one of the given prefixes.
+ function matchPrefixes(prefixes,statuses,resolutions) {
+ var failedPrefixes = new Array();
+ var foundMatch = false;
+ for (var j=0; j<prefixes.length; j++) {
+ var ok1 = addPrefixMatches(prefixes[j],statuses,bug_status);
+ var ok2 = addPrefixMatches(prefixes[j],resolutions,resolution);
+ if ((! ok1) && (! ok2)) {
+ add(prefixes[j],failedPrefixes);
+ } else {
+ foundMatch = true;
+ }
+ }
+ //report an error if some (but not all) prefixes didn't match anything
+ if (foundMatch && (failedPrefixes.length > 0)) {
+ prefixesNotFoundError(failedPrefixes,statuses,resolutions);
+ abort = true;
+ }
+ return foundMatch;
+ }
+
+ if (word[0] == "ALL") {
+ // special case: search for bugs regardless of status
+ addAll(statusResolved,bug_status);
+ word.shift();
+ } else if (word[0] == "OPEN") {
+ // special case: search for open bugs only
+ word.shift();
+ } else if (word[0].match("^[+][A-Z]+(,[A-Z]+)*$")) {
+ // e.g. +DUP,FIX
+ w0 = word.shift();
+ prefixes = w0.substring(1).split(",");
+ if (! matchPrefixes(prefixes,statusResolved,resolutions)) {
+ word.unshift(w0);
+ }
+ } else if (word[0].match("^[A-Z]+(,[A-Z]+)*$")) {
+ // e.g. NEW,ASSI,REOP,FIX
+ bug_status = new Array(); // reset
+ w0 = word.shift();
+ prefixes = w0.split(",");
+ if (! matchPrefixes(prefixes,statusAll,resolutions)) {
+ word.unshift(w0);
+ bug_status = statusOpen.reverse(); //reset to default bug_status
+ }
+ } else {
+ // default case:
+ // search for unresolved bugs only
+ // uncomment this to include duplicate bugs in the search
+ // add("DUPLICATE",resolution);
+ }
+ if (resolution.length > 0) {
+ resolution = resolution.reverse();
+ resolution.unshift("---");
+ addAll(statusResolved,bug_status);
+ }
+ bug_status = bug_status.reverse();
+ bug_status = map(bug_status,escape);
+ searchURL += "?bug_status=" + bug_status.join("&bug_status=");
+ status_and_resolution += 'Status: <tt>'+bug_status+'</tt>';
+
+ if (resolution.length > 0) {
+ resolution = map(resolution,escape);
+ searchURL += "&resolution=" + resolution.join("&resolution=");
+ status_and_resolution += '<br>'+'Resolution: <tt>'+resolution+'</tt>';
+ }
+
+ // end of bug_status & resolution stuff
+
+ var chart = 0;
+ var and = 0;
+ var or = 0;
+
+ var negation = false;
+
+ function negate_comparison_type(type) {
+ switch(type) {
+ case "substring": return "notsubstring";
+ case "anywords": return "nowords";
+ case "regexp": return "notregexp";
+ default:
+ // e.g. "greaterthan"
+ alert("Can't negate comparison type: `" + type + "'");
+ abort = true;
+ }
+ }
+
+ function add_chart(field,type,value) {
+ // undo escaping for value: '"foo%20bar"' --> 'foo bar'
+ var parts = value.split('"');
+ if ((parts.length % 2) != 1) {
+ alert('Internal error: unescaping failure');
+ abort = true;
+ }
+ for (var i=1; i<parts.length; i+=2) {
+ parts[i] = unescape(parts[i]);
+ }
+ var value2 = parts.join('');
+
+ // negate type if negation is set
+ var type2 = type;
+ if (negation) {
+ type2 = negate_comparison_type(type2);
+ }
+ searchURL += make_chart(chart+"-"+and+"-"+or,field,type2,value2);
+ or++;
+ if (negation) {
+ and++;
+ or=0;
+ }
+ }
+
+ for (var i=0; i<word.length; i++, chart++) {
+
+ w = word[i];
+
+ negation = false;
+ if (w[0] == "-") {
+ negation = true;
+ w = w.substring(1);
+ }
+
+ switch (w[0]) {
+ case "+":
+ alternative = w.substring(1).split(/[|,]/);
+ for (var j=0; j<alternative.length; j++)
+ add_chart("short_desc","substring",alternative[j]);
+ break;
+ case "#":
+ alternative = w.substring(1).replace(/[|,]/g," ");
+ add_chart("short_desc","anywords",alternative);
+ if (searchLong)
+ add_chart("longdesc","anywords",alternative);
+ break;
+ case ":":
+ alternative = w.substring(1).split(",");
+ for ( var j=0; j<alternative.length; j++) {
+ add_chart("product","substring",alternative[j]);
+ add_chart("component","substring",alternative[j]);
+ }
+ break;
+ case "@":
+ alternative = w.substring(1).split(",");
+ for ( var j=0; j<alternative.length; j++)
+ add_chart("assigned_to","substring",alternative[j]);
+ break;
+ case "[":
+ add_chart("short_desc","substring",w);
+ add_chart("status_whiteboard","substring",w);
+ break;
+ case "!":
+ add_chart("keywords","anywords",w.substring(1));
+ break;
+ default:
+ alternative=w.split("|");
+ for (var j=0; j<alternative.length; j++) {
+
+ w=alternative[j];
+
+ // votes:xx ("at least xx votes")
+ if (w.match("^votes[:][0-9]+$")) {
+ n = w.split(/[:]/)[1];
+ add_chart("votes","greaterthan",String(n-1));
+ continue;
+ }
+ // generic field1,field2,field3:value1,value2 notation
+ if (w.match("^[^:]+[:][^:\/][^:]*$")) {
+ parts = w.split(":");
+ fields = parts[0].split(/[,]+/);
+ values = parts[1].split(/[,]+/);
+ for (var k=0; k<fields.length; k++) {
+ field = mapField(fields[k]);
+ if (field == undefined) {
+ alert("`"+fields[k]+"'"+
+ " is not a valid field name.");
+ abort = true;
+ return undefined;
+ } else {
+ for (var l=0; l<values.length; l++) {
+ add_chart(field,"substring",values[l]);
+ }
+ }
+ }
+ continue;
+ }
+ comma_separated_words=w.split(/[,]+/);
+ for (var k=0; k<comma_separated_words.length; k++) {
+ w=comma_separated_words[k];
+
+ // platform
+ if (is_platform(w)) {
+ add_chart("rep_platform","substring",w);
+ continue;
+ }
+ // priority
+ if (w.match("^[pP][1-5](,[pP]?[1-5])*$")) {
+ expr = "["+w.replace(/[p,]/g,"")+"]";
+ add_chart("priority","regexp",expr);
+ continue;
+ }
+ if (w.match("^[pP][1-5]-[1-5]$")) {
+ expr = "["+w.substring(1)+"]";
+ add_chart("priority","regexp",expr);
+ continue;
+ }
+ // severity
+ if (is_severity(w)) {
+ add_chart("bug_severity","substring",w);
+ continue;
+ }
+ // votes>xx
+ if (w.match("^votes>[0-9]+$")) {
+ n = w.split(">")[1];
+ add_chart("votes","greaterthan",n);
+ continue;
+ }
+ // votes>=xx, votes=>xx
+ if (w.match("^votes(>=|=>)[0-9]+$")) {
+ separator = w.match("^votes(>=|=>)[0-9]+$")[1];
+ n = w.split(separator)[1];
+ add_chart("votes","greaterthan",String(n-1));
+ continue;
+ }
+ // really default case
+ if (match_product(w)) {
+ add_chart("product","substring",w);
+ }
+ if (match_component(w)) {
+ add_chart("component","substring",w);
+ }
+ if (is_keyword(w)) {
+ add_chart("keywords","substring",w);
+ if (w.length > 2) {
+ add_chart("short_desc","substring",w);
+ add_chart("status_whiteboard","substring",w);
+ }
+ } else {
+ add_chart("short_desc","substring",w);
+ add_chart("status_whiteboard","substring",w);
+ }
+ if (searchLong)
+ add_chart("longdesc","substring",w);
+
+ // URL field (for IP addrs, host.names, scheme://urls)
+ if (w.match(/[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+/)
+ || w.match(/^[A-Za-z]+([.][A-Za-z]+)+/)
+ || w.match(/[:][\/][\/]/)
+ || w.match(/localhost/)
+ || w.match(/mailto[:]?/)
+ // || w.match(/[A-Za-z]+[:][0-9]+/) //host:port
+ )
+ add_chart("bug_file_loc","substring",w);
+ }
+ }
+ }
+
+ or = 0;
+ }
+
+ //searchURL += "&cmdtype=doit";
+
+ if (abort == false) {
+ return searchURL;
+ } else {
+ return undefined;
+ }
+}
+
+function unique_id () {
+ return (new Date()).getTime();
+}
+
+function ShowURL(mode) {
+ var input = document.f.id.value;
+ var searchURL = make_query_URL(bugzilla+"buglist.cgi", input, false);
+ if (searchURL != undefined) {
+ var pieces = searchURL.replace(/[\?]/g,"\n?").replace(/[\&]/g,"\n&");
+ if (mode == "alert") {
+ alert(pieces);
+ } else {
+ var table = "<table border=1>" +
+ "<thead>" +
+ "<tr>" +
+ "<th>Chart-And-Or</th>" +
+ "<th>Field</th>" +
+ "<th>Type</th>" +
+ "<th>Value</th>" +
+ "</tr>" +
+ "</thead>" +
+ "<tbody>" + charts + "</tbody>" +
+ "</table>";
+ var html = '<html>' +
+ '<head>' +
+ '<title>' + input + '</title>' +
+ '</head>' +
+ '<body>' +
+ '<a href="' + searchURL + '">' +
+ 'Submit Query' +
+ '</a>' +
+ '<p>' + status_and_resolution +
+ '<p>' + table +
+ '<pre>' +
+ pieces.replace(/[\n]/g,"<br>") +
+ '</pre>' +
+ '</body>' +
+ '</html>';
+ var w = window.open("","preview_"+unique_id());
+ w.document.write(html);
+ w.document.close();
+ }
+ }
+}
+
+//
+// new interface:
+// searchLong is a boolean now (not a checkbox/radiobutton)
+//
+function Search(url, input, searchLong) {
+ var inputstring = new String(input);
+ var word = inputstring.split(/[\s]+/);
+
+ // Check for empty input
+ if ( word.length == 1 && word[0] == "" )
+ return;
+
+ // Check for potential Bugzilla-busting intensive queries
+ if ((searchLong!=false) && word.length > 4) {
+ var message = "Searching Descriptions for more than four words " +
+ "will take a very long time indeed. Please choose " +
+ "no more than four keywords for your query.";
+ alert(message);
+ return;
+ }
+ var searchURL = make_query_URL(url, inputstring, searchLong);
+ if (searchURL != undefined) {
+ go_to(searchURL);
+ //window.open(searchURL, "other" );
+ } else {
+ return;
+ }
+}
+
+//
+// original interface, untested
+//
+//function SearchForBugs (input, searchRadio) {
+// if (searchRadio[0].checked) {
+// return Search(bugzilla + "buglist.cgi", input, false);
+// } else {
+// return Search(bugzilla + "buglist.cgi", input, true);
+// }
+//}
+
+// derived from http://www.cs.hmc.edu/~jruderma/s/bugz.html
+
+// QuickSearch combines lookup-by-bug-number and search
+// in a single textbox. It's name must be document.f.id .
+//
+// type nothing:
+// --> go to bugzilla front page
+// type a number:
+// --> go to that bug number
+// type several numbers, separated by commas:
+// --> go to a buglist of just those bug numbers
+// type anything else:
+// --> search summary, product, component, keywords, status whiteboard
+// (and URL if it's an IP address, a host.name, or an absolute://URL)
+
+function QuickSearch ()
+{
+ var input = document.f.id.value;
+
+ if (input == "")
+ {
+ //once this _is_ on http://bugzilla.mozilla.org, it should just return;
+ go_to(bugzilla);
+ }
+ else if (input.match(/^[0-9, ]*$/))
+ {
+ if (input.indexOf(",") == -1) {
+ // only _one_ bug number --> show_bug
+ go_to(bugzilla+"show_bug.cgi?id="+escape(input));
+ } else {
+ // comma-separated bug numbers --> buglist
+ go_to(bugzilla+"buglist.cgi?bug_id="+escape(input)
+ + "&bugidtype=include&order=bugs.bug_id");
+ }
+ }
+ else
+ {
+ Search(bugzilla+"buglist.cgi",input,false);
+ }
+ return;
+}
+
+function LoadQuery() {
+ var input = document.f.id.value;
+ Search(bugzilla+"query.cgi",input,false);
+ return;
+}
+