// ==UserScript== // @name Javadoc Instant Search // @namespace http://jo.zerezo.com/projects/ // @description Instant search for Javadoc class names. // @include */allclasses-frame.html // @include */allclasses-noframe.html // @include */package-frame.html // @include */overview-frame.html // ==/UserScript== // // version 0.5 (2012/11/17) // author Joël THIEFFRY // http://jo.zerezo.com/projects/javadocInstantSearch.html // // This script is distributed under the MIT licence. // http://www.opensource.org/licenses/mit-license.php // (function() { "use strict"; // ID of the container for Javadoc Instant Search var ELEMENT_ID = "javadocInstantSearchElement"; // IDs for regex type selector var ECLIPSE_REGEX_ID = "Eclipse"; var SIMPLIFIED_REGEX_ID = "Simplified"; var PLAIN_REGEX_ID = "Plain"; // Compatibility layer for adding and removing events var addEvent; var removeEvent; if (document.addEventListener) { addEvent = function(element, eventname, funcname) { element.addEventListener(eventname, funcname, false); }; removeEvent = function(element, eventname, funcname) { element.removeEventListener(eventname, funcname, false); }; } else if (document.attachEvent) { addEvent = function(element, eventname, funcname) { element.attachEvent("on" + eventname, funcname); }; removeEvent = function(element, eventname, funcname) { element.detachEvent("on" + eventname, funcname); }; } // Create and insert the Javadoc Instant Search container var install = function() { // Second safegard to prevent double instanciation (while debugging) if (document.getElementById(ELEMENT_ID)) { return true; } // Detection of javadoc format var indexContainer = document.getElementsByClassName("indexContainer"); var table = document.getElementsByTagName("table"); if (!indexContainer && !table) { return; } var isModernFormat = indexContainer.length === 1; // Creation of search elements var searchContainer = document.createElement("p"); searchContainer.id = ELEMENT_ID; var textInput = document.createElement("input"); textInput.type = "text"; textInput.style.backgroundColor = "White"; var eraseIcon = document.createElement("input"); eraseIcon.type = "image"; // Origin: http://www.teria.com/~koseki/tools/gm/javadoc_isearch/index.html eraseIcon.src = "data:image/gif;base64,R0lGODlhDQANAJEDAM%2FPz%2F%2F%2F%2F93d3UpihSH5BAEAAAMALAAAAAANAA0AAAIwnCegcpcg4nIw2sRGDZYnBAWiIHJQRZbec5XXEqnrmXIupMWdZGCXlAGhJg0h7lAAADs%3D"; eraseIcon.alt = "erase search pattern"; eraseIcon.style.marginLeft = "3px"; eraseIcon.style.marginRight = "3px"; var isOverview = document.URL.match("/overview-frame\\.html$", "") !== null; var typeRegex = document.createElement("select"); typeRegex.size = 1; typeRegex.multiple = false; if (!isOverview) { var typeRegexOptionEclipse = document.createElement("option"); typeRegexOptionEclipse.value = ECLIPSE_REGEX_ID; typeRegexOptionEclipse.appendChild(document.createTextNode("Eclipse")); typeRegexOptionEclipse.selected = true; typeRegex.add(typeRegexOptionEclipse); } var typeRegexOptionSimplifiedRegex = document.createElement("option"); typeRegexOptionSimplifiedRegex.value = SIMPLIFIED_REGEX_ID; typeRegexOptionSimplifiedRegex.appendChild(document.createTextNode("Simplified")); typeRegexOptionSimplifiedRegex.selected = isOverview; typeRegex.add(typeRegexOptionSimplifiedRegex); var typeRegexOptionPlainRegex = document.createElement("option"); typeRegexOptionPlainRegex.value = PLAIN_REGEX_ID; typeRegexOptionPlainRegex.appendChild(document.createTextNode("Regex")); typeRegex.add(typeRegexOptionPlainRegex); // Insertion of search elements searchContainer.appendChild(textInput); searchContainer.appendChild(eraseIcon); searchContainer.appendChild(typeRegex); if (isModernFormat) { indexContainer = indexContainer[0]; indexContainer.insertBefore(searchContainer, indexContainer.firstChild); } else { table = table[0]; table.parentNode.insertBefore(searchContainer, table); } // Handle the timeout for text input var searchTimeoutHandle = null; var setSearchTimeout = function() { if (searchTimeoutHandle) { clearTimeout(searchTimeoutHandle); } searchTimeoutHandle = setTimeout(runSearch, 200); // in milliseconds }; // Run the search var runSearch = function() { if (searchTimeoutHandle) { clearTimeout(searchTimeoutHandle); searchTimeoutHandle = null; } var acceptFunction = function() { return true; }; if (textInput.value) { var regex = textInput.value; var caseInvariantSwitch = ""; //console.log("input = " + regex); var selectedTypeRegex = typeRegex.options[typeRegex.selectedIndex].value; switch (selectedTypeRegex) { case ECLIPSE_REGEX_ID: regex = "^.*" + regex.replace(/([\\\^\$*+\[\]?{}.=!:(|)])/g, "\\$1") + ".*$"; regex = regex.replace(/([A-Z])/g, ".*$1"); caseInvariantSwitch = "i"; break; case SIMPLIFIED_REGEX_ID: // Escape all except ? and * regex = regex.replace(/([\\\^\$+\[\]{}.=!:(|)])/g, "\\$1"); regex = "^.*" + regex.replace(/(?:\*)+/g, ".*").replace(/(?:\?)+/g, ".") + ".*$"; caseInvariantSwitch = "i"; break; } //console.log("before = " + regex); regex = regex.replace(/((.[\*\?])+)(?=\2)/g, "") // Replace successive x* and x? to only one .replace(/((.)\*\2\?)+/g, ".*") // Replace x*x? with x* .replace(/((.)\?\2\*)+/g, ".*") // Replace x?x* with x* .replace(/((.)\+\2\*)+/g, ".+") // Replace x+x* with x+ .replace(/((.)\*\2\+)+/g, ".+") // Replace x*x+ with x+ .replace(/((.[\*\?])+)(?=\2)/g, ""); // Replace successive x* and x? to only one //console.log("after = " + regex); try { var searchRegexp = new RegExp(regex, caseInvariantSwitch); } catch (err) { textInput.style.backgroundColor = "Tomato"; return false; } textInput.style.backgroundColor = "Lavender"; acceptFunction = function(name) { return searchRegexp.test(name); }; } else { textInput.style.backgroundColor = "White"; } if (isModernFormat) { var origUl = indexContainer.getElementsByTagName("ul")[0]; var clonedUl = origUl.cloneNode(true); (function(items) { for (var iItem = 0; iItem < items.length; iItem++) { var liItem = items[iItem]; var itemText = liItem.firstChild.firstChild; if (itemText.tagName === "I") { itemText = itemText.firstChild; } liItem.style.display = acceptFunction(itemText.nodeValue) ? "" : "none"; } })(clonedUl.getElementsByTagName("li")); indexContainer.replaceChild(clonedUl, origUl); origUl.innerHTML = ""; } else { var origTBody = table.getElementsByTagName("tbody")[0]; var clonedTBody = origTBody.cloneNode(true); (function(items) { for (var iItem = 0; iItem < items.length; iItem++) { var aItem = items[iItem]; var itemText = aItem.firstChild; if (itemText.tagName === "I") { itemText = itemText.firstChild; } var displayStyle = acceptFunction(itemText.nodeValue) ? "" : "none"; aItem.style.display = displayStyle; var nextBr = aItem.nextSibling; while (nextBr.nodeType !== 1) { nextBr = nextBr.nextSibling; } if (nextBr.tagName === "BR") { nextBr.style.display = displayStyle; } } })(clonedTBody.getElementsByTagName("a")); table.replaceChild(clonedTBody, origTBody); origTBody.innerHTML = ""; } textInput.focus(); return false; }; // Change the type of regex var changeRegexType = function() { if (textInput.value !== "") { runSearch(); // will clear the timeout } else { textInput.focus(); return false; } }; // Erase the current input pattern var eraseSearch = function() { if (textInput.value !== "") { textInput.value = ""; runSearch(); // will clear the timeout } else { textInput.focus(); return false; } }; // Install all event listeners addEvent(textInput, "input", setSearchTimeout); addEvent(typeRegex, "change", changeRegexType); addEvent(eraseIcon, "click", eraseSearch); // Uninstall all event listeners var uninstall = function() { removeEvent(textInput, "input", setSearchTimeout); removeEvent(typeRegex, "change", changeRegexType); removeEvent(eraseIcon, "click", eraseSearch); }; addEvent(window, "unload", uninstall); textInput.focus(); return true; }; // Install the component when window has done loading, and the component is not already here if (!document.getElementById(ELEMENT_ID)) { install(); } })();