Skip to content

Instantly share code, notes, and snippets.

@basyura
Created September 8, 2011 04:33
Show Gist options
  • Save basyura/1202627 to your computer and use it in GitHub Desktop.
Save basyura/1202627 to your computer and use it in GitHub Desktop.
javadoc_incremental_search
// ==UserScript==
// @name Javadoc Incremental Search
// @namespace http://basyura.org
// @description Incremental search for Javadoc Class names.
// @include */allclasses-frame.html
// ==/UserScript==
//
// version 0.1
//
// original :
// http://kengo.z1.bbzone.net/
// Copyright (c) 2006 KOSEKI Kengo
// http://www.teria.com/~koseki/tools/gm/javadoc_isearch/index.html
//
// This script is distributed under the MIT licence.
// http://www.opensource.org/licenses/mit-license.php
//
(function() {
const SEARCH_ACCESS_KEY = "i";
const XPATH_HEADING = "//font[@class='FrameHeadingFont']/b";
const XPATH_CONTAINER = "//font[@class='FrameItemFont']";
const XPATH_MENU_LINK = "//li/a";
const AUTO_OPEN = false;
const MENU =
"<li><a href='http://www.docjar.com/s.jsp?q=##CLASS_NAME##' target='classFrame'>@1:search(Docjar)</a></li>"
+ "<li><a href='http://www.docjar.com/html/api/##PACKAGE_PATH##/##CLASS_NAME##.java.html' target='classFrame'>@2:source(Docjar)</a></li>"
+ "<li><a href='http://www.koders.com/?s=##PACKAGE_NAME##+##CLASS_NAME##+##ANCHOR_NAME##' target='classFrame'>@3:search(koders)</a></li>";
const MENU_REPLACEMENT = {
CLASS_NAME: function(classLink) {
return classLink.name;
},
FQCN: function(classLink) {
return classLink.getFqcn();
},
PACKAGE_NAME: function(classLink) {
return classLink.getPackageName();
},
PACKAGE_PATH: function(classLink) {
return classLink.getPackageName().replace(/\./g, "/");
},
ANCHOR_NAME: function(classLink, anchorLink) {
if (anchorLink == null) {
return "";
}
return anchorLink.getNameWithoutParameter();
}
};
var allClassLinks = new Array();
var topClassLink = null;
var topAnchorLink = null;
var initialized = false;
var isAll = false;
var lastAutoOpenUrl = null;
var view = new View();
var query = new Query(view);
var anchorsLoader = new AnchorsLoader();
var anchorsCache = new AnchorsCache();
/*
* main
*/
function init() {
view.initContainer();
allClassLinks = getAllClassLinks();
if (allClassLinks.length <= 0) {
return false;
}
topClassLink = allClassLinks[0];
return true;
}
function search() {
if (! initialized) {
return;
}
if (query.isMenuMode()) {
if (query.isModeChanged()) {
showMenu();
} else {
selectMenu();
}
} else if (query.isAnchorMode()) {
if (query.isModeChanged()) {
loadAnchors();
} else {
selectAnchors();
}
} else {
selectClasses();
}
}
function getAllClassLinks() {
var source = document.body.innerHTML;
var rx = /<a [^>]+>(<i\s*>)?([^<]+)(<\/i\s*>)?<\/a\s*>/gi;
var matches;
var classLinks = new Array();
var i = 0;
while ((matches = rx.exec(source)) != null) {
classLinks.push(new ClassLink(matches[2], matches[0], i));
i++;
}
return classLinks;
}
function selectClasses() {
if (query.isSelectAll()) {
if (isAll && ! query.isModeChanged()) {
return;
}
isAll = true;
} else {
isAll = false;
}
var container = view.getContainer();
if (isAll) {
container.setOriginal();
topClassLink = allClassLinks[0];
return;
}
var condition = query.createCondition();
var node = container.createContentNode();
appendClasses(condition, node);
container.setContentNode(node);
}
function appendClasses(condition, parent) {
topClassLink = null;
var html = "";
var count = 0;
for (var i = 0; i < allClassLinks.length; i++) {
var cl = allClassLinks[i];
if (condition(cl)) {
count++;
html += cl.html;
if (topClassLink == null) {
topClassLink = allClassLinks[i];
}
}
}
if (count == 1 && AUTO_OPEN) {
var url = topClassLink.getUrl();
if (url != lastAutoOpenUrl) {
lastAutoOpenUrl = url;
openInClassFrame(url);
}
}
parent.innerHTML = html;
}
function loadAnchors() {
view.selectClass(topClassLink);
view.getSubContainer().print("loading...");
anchorsLoader.load(topClassLink);
}
function selectAnchors() {
if (topClassLink == null || ! anchorsCache.contains(topClassLink)) {
return;
}
var condition = query.createCondition();
var container = view.getSubContainer();
var node = container.createContentNode();
anchorsCache.appendAnchors(node, topClassLink, condition);
container.setContentNode(node);
}
function updateAnchors() {
if (query.isAnchorMode()) {
selectAnchors(query.getSearchString());
}
}
function openInClassFrame(url) {
try {
if (unsafeWindow.parent.frames[2] != null) {
unsafeWindow.parent.frames[2].location.href = url;
unsafeWindow.parent.frames[2].focus();
return true;
}
} catch (ex) {
log(ex);
}
return false;
}
function showMenu() {
view.selectClass(topClassLink);
var container = view.getSubContainer();
var node = container.createContentNode();
var content = MENU;
var rx = /##(\w+)##/;
var matches;
while ((matches = rx.exec(content)) != null) {
var f = MENU_REPLACEMENT[matches[1]];
var rx2 = new RegExp("##" + matches[1] + "##", "g");
if (f == null) {
content = content.replace(rx2, "");
} else {
var anchorLink = null;
if (query.isAnchorSearchStarted()) {
anchorLink = topAnchorLink;
}
content = content.replace(rx2, f(topClassLink, anchorLink));
}
}
node.innerHTML = content;
container.setContentNode(node);
}
function selectMenu() {
if (query.getSearchString() == "") {
return;
}
var node = view.getSubContainer().getNode();
var xpathResult = document.evaluate(XPATH_MENU_LINK, node, null,
XPathResult.ANY_TYPE, null);
var node;
while ((node = xpathResult.iterateNext()) != null) {
var textNode = node.firstChild;
if (textNode != null
&& textNode.nodeType == 3 /* Node.TEXT_NODE */
&& textNode.nodeValue.indexOf("@" + query.getSearchString()) == 0) {
openMenu(node);
query.input("");
search();
return;
}
}
query.update("@");
}
function openMenu(node) {
var href = node.getAttribute("href");
openInClassFrame(href);
}
/**
* ClassLink
*/
function ClassLink(name, html, idx) {
this.name = name;
this.html = html + "<br/>";
this.lowerName = this.name.toLowerCase();
this.url = null;
this.packageName = null;
}
ClassLink.prototype.getUrl = function() {
if (this.url != null) {
return this.url;
}
var rx = /href\s*=\s*(?:"|')([^"']+)(?:"|')/;
var matches;
if ((matches = rx.exec(this.html)) != null) {
this.url = matches[1];
return this.url;
}
return null;
}
ClassLink.prototype.getPackageName = function() {
if (this.packageName == null) {
this.packageName = this._createPackageName();
}
return this.packageName;
}
ClassLink.prototype.getFqcn = function() {
var packageName = this.getPackageName();
if (packageName == "") {
return this.name;
} else {
return packageName + "." + this.name;
}
}
ClassLink.prototype._createPackageName = function() {
var base = this._getDocumentBase();
var url = this.getUrl();
if (url.indexOf(base) == 0) {
url = url.substring(base.length + 1);
}
url = url.substring(0, url.lastIndexOf("/"));
return url.replace(/\//g,".");
}
ClassLink.prototype._getDocumentBase = function() {
if (this.documentBase != null) {
return this.documentBase;
}
var url = document.location.href;
url = url.substring(0, url.length - "/allclasses-frame.html".length);
ClassLink.prototype.documentBase = url;
return url;
}
/**
* Query
*/
function Query(view) {
this.mode = 1;
this.modeChanged = false;
this.search = "";
this.lastClassSearch = "";
this.lastAnchorSearch = "";
this.view = view;
}
Query.CLASS_MODE = 1;
Query.ANCHOR_MODE = 2;
Query.MENU_MODE = 3;
Query.prototype.getSearchString = function() {
return this.search;
}
Query.prototype.isModeChanged = function() {
return this.modeChanged;
}
Query.prototype.isSelectAll = function() {
return (this.search.length == 0 || this.search == "*");
}
Query.prototype.isAnchorSearchStarted = function() {
if (this.isAnchorMode()) {
return (0 < this.searchString.length);
} else if (this.isMenuMode()) {
return (1 < this.lastAnchorSearch.length); // lastAnchorSearch starts with '#'
}
return false;
}
Query.prototype.createCondition = function() {
var q = this.search;
if (this.isSelectAll()) {
return function(o){ return true; };
} else if (q.match(/^([A-Z][^A-Z]*){2,}$/) && q.indexOf("*") < 0) {
var pattern = "^" + q.replace(/([A-Z][^A-Z]*)/g, "$1[^A-Z]*");
return function(o) { return o.name.match(pattern); };
} else if (0 < q.lastIndexOf("*")) {
q = q.toLowerCase();
var pattern = "^" + q.replace(/(\W)/g, "\\$1").replace(/\\\*/g, ".*");
return function(o) { return o.lowerName.match(pattern); };
} else if (q.indexOf("*") == 0) {
q = q.substring(1);
q = q.toLowerCase();
return function(o) { return o.lowerName.indexOf(q) != -1; };
} else {
q = q.toLowerCase();
return function(o) { return o.lowerName.indexOf(q) == 0; };
}
}
Query.prototype.input = function(input) {
var lastMode = this.mode;
input = this._shiftMode(input);
this.modeChanged = (lastMode != this.mode);
this.search = this._getSearchStringFromInput(input);
}
Query.prototype.update = function(input) {
this.view.setFieldValue(input);
this.input(input);
}
Query.prototype._getSearchStringFromInput = function(input) {
if (this.isMenuMode()) {
if (input.length <= 1) {
return "";
} else {
return input.substring(1, 2);
}
} else if (this.isAnchorMode()) {
if (0 < input.lastIndexOf("#")) {
view.setFieldValue("#");
return "";
} else {
input = input.substring(1);
return this._normalize(input);
}
} else if (this.isClassMode()) {
return this._normalize(input);
} else {
return "";
}
}
Query.prototype._normalize = function(input) {
input = this._concatStars(input);
input = this._removeLastStar(input);
return input;
}
Query.prototype._shiftMode = function(input) {
if (input.indexOf("@") != -1) {
if (this.isMenuMode()) {
return input;
}
// * -> menuMode
var lastSearch = input.replace(/@/g, "");
this._memoryLastSearch(lastSearch);
this.view.setFieldValue("@");
this.mode = Query.MENU_MODE;
return "@";
} else if (input.indexOf("#") != -1) {
if (this.isAnchorMode()) {
return input;
}
// * -> anchorMode
var lastSearch = input.replace(/#/g, "");
this._memoryLastSearch(lastSearch);
this.view.setFieldValue("#");
this.mode = Query.ANCHOR_MODE;
return "#";
} else if (this.isMenuMode() && this.lastAnchorSearch != "") {
// menuMode -> anchorMode
this.view.setFieldValue(this.lastAnchorSearch);
input = this.lastAnchorSearch;
this.lastAnchorSearch = "";
this.mode = Query.ANCHOR_MODE;
return input;
} else if (! this.isClassMode()) {
// * -> classMode
this.view.setFieldValue(this.lastClassSearch);
input = this.lastClassSearch;
this.lastAnchorSearch = "";
this.lastClassSearch = "";
this.mode = Query.CLASS_MODE;
return input;
}
return input;
}
Query.prototype._memoryLastSearch = function(lastSearch) {
if (this.isClassMode()) {
this.lastClassSearch = lastSearch;
this.lastAnchorSearch = "";
this.search = "";
} else if (this.isAnchorMode()) {
this.lastAnchorSearch = lastSearch;
this.search = "";
}
}
Query.prototype._removeLastStar = function(s) {
if (s.lastIndexOf("*") == s.length - 1) {
s = s.substring(0, s.length - 1);
}
return s;
}
Query.prototype._concatStars = function(s) {
return s.replace(/\*+/, "*");
}
Query.prototype.isClassMode = function() {
return this.mode == Query.CLASS_MODE;
}
Query.prototype.isAnchorMode = function() {
return this.mode == Query.ANCHOR_MODE;
}
Query.prototype.isMenuMode = function() {
return this.mode == Query.MENU_MODE;
}
/**
* View
*/
function View() {
this.field = null;
this.container = null;
this.subContainer = null;
}
View.prototype.getContainer = function() {
return this.container;
}
View.prototype.getSubContainer = function() {
return this.subContainer;
}
View.prototype.setFieldValue = function(v) {
this.field.value = v;
}
View.prototype.getFieldValue = function() {
return this.field.value;
}
View.prototype.getFieldElement = function() {
return this.field;
}
View.prototype.focusField = function() {
this.field.focus();
}
View.prototype.selectClass = function(classLink) {
var node = this.container.createContentNode();
node.innerHTML = classLink.html;
node.appendChild(this.subContainer.getParent());
this.container.setContentNode(node);
}
View.prototype.initSearchField = function() {
var node = this._getHeadingNode();
if (node == null) {
return;
}
node.removeChild(node.firstChild);
this.field = this._createSearchField();
node.appendChild(this.field);
}
View.prototype.initContainer = function() {
var xpathResult = selectAnyType(XPATH_CONTAINER);
var node = xpathResult.iterateNext();
if (node == null) {
return false;
}
this.container = new Container(node);
node = this._createSubContainerNode();
this.subContainer = new Container(node);
}
View.prototype._getHeadingNode = function() {
var xpathResult = selectAnyType(XPATH_HEADING);
return xpathResult.iterateNext();
}
View.prototype._createSearchField = function() {
var s = document.createElement("input");
s.style.width = '100%';
s.setAttribute("type", "text");
s.addEventListener("keyup" , function(e) {
if (e.keyCode == 13) {
if (query.isClassMode()) {
openInClassFrame(topClassLink.getUrl());
}
else if (query.isAnchorMode()) {
openInClassFrame(topAnchorLink.getUrl());
}
}
else {
query.input(this.value);
search();
}
} , false);
s.addEventListener("focus" , function(e) {
document.body.scrollLeft = 0;
} , false);
if (SEARCH_ACCESS_KEY != null && SEARCH_ACCESS_KEY != "") {
s.setAttribute("accesskey", SEARCH_ACCESS_KEY);
}
return s;
}
View.prototype._createSubContainerNode = function() {
var parent = document.createElement("span");
var node = document.createElement("ul");
node.setAttribute("style", "list-style-type:none; padding:0");
parent.appendChild(node);
return node;
}
/**
* Container
*/
function Container(masterNode) {
this.parent = masterNode.parentNode;
this.master = masterNode;
this.current = null;
}
Container.prototype.clear = function() {
if (this.parent.hasChildNodes()) {
this.parent.removeChild(this.parent.firstChild);
}
this.current = null;
}
Container.prototype.createContentNode = function() {
return this.master.cloneNode(false);
}
Container.prototype.setContentNode = function(node) {
if (this.parent.hasChildNodes()) {
this.parent.replaceChild(node, this.parent.firstChild);
} else {
this.parent.appendChild(node);
}
this.current = node;
}
Container.prototype.getNode = function() {
return this.current;
}
Container.prototype.getParent = function() {
return this.parent;
}
Container.prototype.print = function(msg) {
var node = document.createTextNode(msg);
this.setContentNode(node);
}
Container.prototype.setOriginal = function() {
this.setContentNode(this.master);
}
/**
* AnchorsLoader
*/
function AnchorsLoader() {
}
AnchorsLoader.prototype.load = function(classLink) {
if (anchorsCache.contains(classLink)) {
updateAnchors();
return;
}
var handler = new AnchorsRequestHandler();
try {
var req = new XMLHttpRequest();
req.open("GET", classLink.getUrl(), true);
req.onreadystatechange = function() {
if (req.readyState == 2) {
handler.loaded(req, classLink);
}
else if (req.readyState == 4 && req.responseText) {
handler.completed(req, classLink);
}
};
req.send("");
} catch(e) {
var p = new Object();
p.method = "GET";
p.url = classLink.getUrl();
p.onreadystatechange = function(res) {
if (res.readyState == 2) {
handler.loaded(res, classLink);
}
else if (res.readyState == 4 && res.responseText) {
handler.completed(res, classLink);
}
}
GM_xmlhttpRequest(p);
}
}
/**
* AnchorsRequestHandler
*/
function AnchorsRequestHandler() {
}
AnchorsRequestHandler.prototype.loaded = function(req, classLink) {
view.getSubContainer().print("parsing...");
}
AnchorsRequestHandler.prototype.completed = function(req, classLink) {
if (! query.isAnchorMode() || classLink != topClassLink) {
return;
}
var names = this._getAnchorNames(req.responseText);
var nodes = this._createAnchorLinkArray(classLink.getUrl(), names);
anchorsCache.add(classLink, nodes);
updateAnchors();
}
AnchorsRequestHandler.prototype._createAnchorLinkArray = function(baseurl,
names) {
var nodes = new Array();
var keywordNodes = new Array();
for (var i = 0; i < names.length; i++) {
var node = new AnchorLink(baseurl, names[i]);
if (node.isKeyword()) {
keywordNodes.push(node);
} else {
nodes.push(node);
}
}
for (var i = 0; i < keywordNodes.length; i++) {
nodes.push(keywordNodes[i]);
}
return nodes;
}
AnchorsRequestHandler.prototype._getAnchorNames = function(doc) {
var pat = /<A NAME=\"([^\"]+)\"/gi;
var i = 0;
var matches;
var names = new Array();
while ((matches = pat.exec(doc)) != null) {
names.push(matches[1]);
}
return names;
}
/**
* AnchorLink
*/
function AnchorLink(baseurl, name) {
this.name = name;
this.lowerName = name.toLowerCase();
this.url = baseurl + "#" + name;
this.keywordOrNot = this._getKeywordOrNot(name);
this.html = this._getHtml(name, this.url, this.keywordOrNot);
}
AnchorLink.prototype.getLowerName = function() {
return this.lowerName;
}
AnchorLink.prototype.getUrl = function() {
return this.url;
}
AnchorLink.prototype.isKeyword = function() {
return this.keywordOrNot;
}
AnchorLink.prototype.getNameWithoutParameter = function() {
if (this.name.indexOf("(") != -1) {
return this.name.substring(0, this.name.indexOf("("));
} else {
return this.name;
}
}
AnchorLink.keywords = {
"navbar_top" : 1,
"navbar_top_firstrow" : 1,
"skip-navbar_top" : 1,
"field_summary" : 1,
"nested_class_summary" : 1,
"constructor_summary" : 1,
"constructor_detail" : 1,
"method_summary" : 1,
"method_detail" : 1,
"field_detail" : 1,
"navbar_bottom" : 1,
"navbar_bottom_firstrow" : 1,
"skip-navbar_bottom" : 1
};
AnchorLink.keywordPrefixes = [
"methods_inherited_from_",
"fields_inherited_from_" ,
"nested_classes_inherited_from_"
];
AnchorLink.prototype._getKeywordOrNot = function(name) {
if (AnchorLink.keywords[name] == 1) {
return true;
}
for (var i = 0 ; i < AnchorLink.keywordPrefixes.length ; i++) {
if (name.indexOf(AnchorLink.keywordPrefixes[i]) == 0) {
return true;
}
}
return false;
}
AnchorLink.prototype._getHtml = function(name, url, keywordOrNot) {
var html = "<li><a href=\"" + url + "\" target=\"classFrame\" class=\"anchorLink\"";
if (keywordOrNot) {
html += " style=\"color:#666\"";
}
html += ">" + name + "</a></li>";
return html;
}
/**
* AnchorsCache
*/
function AnchorsCache() {
this.cache = new Array();
}
AnchorsCache.prototype.add = function(classLink, anchors) {
this.cache[classLink.getUrl()] = anchors;
}
AnchorsCache.prototype.contains = function(classLink) {
return (this.cache[classLink.getUrl()] != null);
}
AnchorsCache.prototype.appendAnchors = function(parent, classLink, condition) {
var anchorLinks = this.cache[classLink.getUrl()];
if (anchorLinks == null) {
return;
}
topAnchorLink = null;
var html = "";
var count = 0;
for (var i = 0 ; i < anchorLinks.length ; i++) {
var al = anchorLinks[i];
if (condition(al)) {
count++;
html += al.html;
if (topAnchorLink == null) {
topAnchorLink = al;
}
}
}
if (count == 1 && AUTO_OPEN && ! query.isModeChanged()) {
var url = topAnchorLink.getUrl();
if (url != lastAutoOpenUrl) {
lastAutoOpenUrl = url;
openInClassFrame(url);
}
}
parent.innerHTML = html;
}
view.initSearchField();
view.focusField();
initialized = init();
/*
* utils
*/
function selectAnyType(xpath) {
return document.evaluate(xpath, document, null,
XPathResult.ANY_TYPE, null);
}
var framesets = parent.document.getElementsByTagName("frameset");
framesets[1].rows = '0%,100%';
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment