Javadoc Search Frame

By Steven G Brown Last update Mar 3, 2008 — Installed 170 times.

Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)

// ==UserScript==
// @name          Javadoc Search Frame
// @namespace     http://userscripts.org/users/46156
// @description   Incremental search frame for Javadoc packages and classes. Last updated 3rd March 2008.
// @include       */allclasses-frame.html
// ==/UserScript==
// 
// Copyright (c) 2008 Steven G Brown
// 
// Modified from Javadoc Incremental Search version 0.5:
// http://www.teria.com/~koseki/tools/gm/javadoc_isearch/index.html
// Copyright (c) 2006 KOSEKI Kengo
// 
// Changes from Javadoc Incremental Search version 0.5:
// - The packages frame and the classes frame are merged into a single frame.
//   The search field allows searching all packages and classes at once.
//   Classes can be searched for by simple name or canonical name.
// - When the search field has the focus, the CTRL+Enter key combination will
//   open the package or class displayed at the top of the search frame in a
//   new tab.
// - The escape key can be used to clear the search field.
// - Added Firefox user preferences to allow the behaviour of the script to be
//   customised:
//     auto_open -    true to automatically open the first package or class
//                    in the list after each search, false to wait for the user
//                    to open the package or class manually. Default is false.
//     class_menu -   menu displayed when pressing the '@' key if a class is
//                    currently displayed at the top of the search list.
//     package_menu - menu displayed when pressing the '@' key if a package is
//                    currently displayed at the top of the search list.
//     log          - true to display logging information to the Firefox Error
//                    Console, false to not display this information. Default
//                    is false.
// - A new search is performed when the user has finished typing characters
//   into the search field, rather than after each keypress.
// - Removed usages of unsafeWindow.
// 
// This script requires Firefox 1.5 or later.
// 
// This script is distributed under the MIT licence.
// http://www.opensource.org/licenses/mit-license.php
//

(function() {

String.prototype.endsWith = function (s) {
    var strIndex = this.length - s.length;
    return strIndex >= 0 && this.substring(strIndex) == s;
};

const SEARCH_ACCESS_KEY = "s";
const ERASE_ACCESS_KEY = "a";

const XPATH_HEADING = "//font[@class='FrameHeadingFont']/b";
const XPATH_CONTAINER = "//font[@class='FrameItemFont']";
const XPATH_MENU_LINK = "//a";

const ICON_ERASE = "data:image/gif;base64,R0lGODlhDQANAJEDAM%2FPz%2F%2F%2F%2F93d3UpihSH5BAEAAAMALAAAAAANAA0AAAIwnCegcpcg4nIw2sRGDZYnBAWiIHJQRZbec5XXEqnrmXIupMWdZGCXlAGhJg0h7lAAADs%3D";

const MENU_REPLACEMENT = {
    CLASS_NAME: function(classLink) { 
        return classLink.className;
    },

    PACKAGE_NAME: function(classLink) { 
        return classLink.packageName; 
    },

    PACKAGE_PATH: function(classLink) { 
        return classLink.packageName.replace(/\./g, "/");
    },

    ANCHOR_NAME: function(classLink, anchorLink) {
        if (anchorLink == null) {
            return "";
        }
        return anchorLink.getNameWithoutParameter();
    }
};

const AUTO_OPEN_PREFERENCE = new UserPreference("auto_open", false);

const PACKAGE_MENU_PREFERENCE = new UserPreference("package_menu",
"<a href='http://www.koders.com/?s=##PACKAGE_NAME##' target='classFrame'>@1:search(koders)</a><br/>" +
"<a href='http://www.docjar.com/s.jsp?q=##PACKAGE_NAME##' target='classFrame'>@2:search(Docjar)</a><br/>");

const CLASS_MENU_PREFERENCE = new UserPreference("class_menu",
"<a href='http://www.koders.com/?s=##PACKAGE_NAME##+##CLASS_NAME##+##ANCHOR_NAME##' target='classFrame'>@1:search(koders)</a><br/>" +
"<a href='http://www.docjar.com/s.jsp?q=##CLASS_NAME##' target='classFrame'>@2:search(Docjar)</a><br/>" +
"<a href='http://www.docjar.com/html/api/##PACKAGE_PATH##/##CLASS_NAME##.java.html' target='classFrame'>@3:source(Docjar)</a><br/>");

const LOG_PREFERENCE = new UserPreference("log", false);

var allClassLinks = new Array();
var currentClassLinks = new Array();
var topClassLink = null;
var topAnchorLink = null;
var defaultHTML = null;
var initialized = false;
var isAll = false;
var lastAutoOpenUrl = null;

var view = new View();
var query = new Query(view);
var previousQuery = null;
var anchorsLoader = new AnchorsLoader();
var anchorsCache = new AnchorsCache();

/*
 * main
 */
function init() {
    // If a user preference does not exist, write the default value.
    AUTO_OPEN_PREFERENCE.get();
    PACKAGE_MENU_PREFERENCE.get();
    CLASS_MENU_PREFERENCE.get();
    LOG_PREFERENCE.get();

    // Initialise stored class links.
    allClassLinks = getAllClassLinks();
    if (allClassLinks.length <= 0) {
        return false;
    }
    for (i = 0; i < allClassLinks.length; i++) {
        currentClassLinks.push(i);
    }
    topClassLink = getTopClassLink();

    // Initialise class frame.
    view.initContainer();
    var container = view.getContainer();
    var node = container.createContentNode();
    container.setContentNode(node);
    node.innerHTML = defaultHTML;
    view.initSearchField();

    // Hide the package list frame.
    var frameset = top.document.getElementsByTagName("frameset")[0].getElementsByTagName("frameset")[0];
    if (frameset != undefined) {
        frameset.setAttribute("rows", "0,*");
        frameset.setAttribute("border", 0);
        frameset.setAttribute("frameborder", 0);
        frameset.setAttribute("framespacing", 0);
        scroll(0, 0);
    }

    // Give the search field focus.
    view.focusField();

    return true;
}

function search() {
    logTimeStart();
    if (! initialized) {
        return;
    }

    if (query.isMenuMode()) {
        if (query.isModeChanged()) {
            showMenu();
        } else {
            selectMenu();
        }
    } else if (query.isAnchorMode()) {
        if (query.isModeChanged()) {
            loadAnchors();
        } else {
            selectAnchors();
        }
    } else {
        selectClasses();
    }
    logTimeStop();
}

function getAllClassLinks() {
    var classLinks = new Array();
    var cl;
    var i = 0;
    var matches;
    defaultHTML = "";

    // add packages to class links
    var packagesHeader = getHeaderClassLink("<b>Packages</b>", i);
    classLinks.push(packagesHeader);
    defaultHTML += packagesHeader.html;

    var packageFrame = top.frames[0];
    if (packageFrame.name == "packageListFrame") {
        var packagesInnerHtml = packageFrame.document.body.innerHTML;
        var packagesRegex = /<a [^>]+>([^<]+)<\/a\s*>/gi;
        while ((matches = packagesRegex.exec(packagesInnerHtml)) != null) {
            if (matches[1] != "All Classes") {
                var html = matches[0]
                        .replace("package-frame.html", "package-summary.html")
                        .replace("target=\"packageFrame\"", "target=\"classFrame\"");
                cl = getPackageClassLink(matches[1], html, i);
                classLinks.push(cl);
                defaultHTML += cl.html;
                i++;
            }
        }
    }

    // add interfaces, classes, enumerations, and annotations to class links
    var classLinksMap = new Array();
    var classLinksDefaultHTMLMap = new Array();
    var classLinksMapKeys = new Array("interface", "class", "enum", "exception", "error", "annotation");
    for (keyIndex = 0; keyIndex < classLinksMapKeys.length; keyIndex++) {
        classLinksMap[classLinksMapKeys[keyIndex]] = new Array();
        classLinksDefaultHTMLMap[classLinksMapKeys[keyIndex]] = "";
    }
    var classesInnerHtml = document.body.innerHTML;
    var classesRegex = /<a [^>]*title=\"([^\s]+) in ([^\"]+)[^>]*>(<i\s*>)?([^<]+)(<\/i\s*>)?<\/a\s*>/gi;
    var type;
    while ((matches = classesRegex.exec(classesInnerHtml)) != null) {
        cl = getClassLink(
                matches[2], matches[4], matches[0] + " [ " + matches[2] + " ]", i);
        type = matches[1];
        if (type == "class") {
             if (matches[4].endsWith("Exception")) {
                 type = "exception";
             } else if (matches[4].endsWith("Error")) {
                 type = "error";
             }
        }
        classLinksMap[type].push(cl);
        classLinksDefaultHTMLMap[type] += cl.html;
        i++;
    }
    headers = new Array("Interfaces", "Classes", "Enums", "Exceptions", "Errors", "Annotation Types");
    for (i = 0; i < headers.length; i++) {
        var headerClassLink = getHeaderClassLink("<br/><b>" + headers[i] + "</b>", i);
        classLinks.push(headerClassLink);
        defaultHTML += headerClassLink.html;
        classLinks = classLinks.concat(classLinksMap[classLinksMapKeys[i]]);
        defaultHTML += classLinksDefaultHTMLMap[classLinksMapKeys[i]];
    }
    
    return classLinks;
}

function getTopClassLink() {
  for (i = 0; i < currentClassLinks.length; i++) {
    var cl = allClassLinks[currentClassLinks[i]];
    if (!cl.isHeader()) {
      return cl;
    }
  }
  return null;
}

function selectClasses() {
    if (previousQuery != null && previousQuery.equals(query)) {
        return;
    }

    var container = view.getContainer();
    var node = container.createContentNode();

    if (query.isSelectAll()) {
        node.innerHTML = defaultHTML;
        container.setContentNode(node);
        currentClassLinks = new Array();
        for (i = 0; i < allClassLinks.length; i++) {
            currentClassLinks.push(i);
        }
        topClassLink = getTopClassLink();
    } else {
        var condition = query.createClassLinkCondition();
        appendClasses(condition, node);
        container.setContentNode(node);
    }

    if (topClassLink != null && AUTO_OPEN_PREFERENCE.get()) {
        var url = topClassLink.getUrl();
        if (url != lastAutoOpenUrl) {
            lastAutoOpenUrl = url;
            openInClassFrame(url);
        }
    }

    previousQuery = new PreviousQuery(query);
}

function appendClasses(condition, parent) {
    if (previousQuery != null && previousQuery.search.length > 0
            && previousQuery.search.indexOf(query.search) == 0) {
        var previousClassLinks = new Array(allClassLinks.length);
        for (var i = 0; i < currentClassLinks.length; i++) {
            previousClassLinks[currentClassLinks[i]] = true;
        }
        currentClassLinks = new Array();
        for (i = 0; i < allClassLinks.length; i++) {
            currentClassLinks.push(i);
        }
        currentClassLinks = currentClassLinks.filter(function(clIndex) {
            return previousClassLinks[clIndex] || condition(allClassLinks[clIndex]);
        });
    } else {
        if (previousQuery == null || query.search.indexOf(previousQuery.search) != 0) {
            currentClassLinks = new Array();
            for (i = 0; i < allClassLinks.length; i++) {
                currentClassLinks.push(i);
            }
        }
        currentClassLinks = currentClassLinks.filter(function(clIndex) {
            var cl = allClassLinks[clIndex];
            return cl.isHeader() || condition(cl);
        });
    }

    topClassLink = getTopClassLink();

    var html = "";
    currentClassLinks.forEach(function(clIndex) {
        html += allClassLinks[clIndex].html;
    });

    parent.innerHTML = html;
}

function loadAnchors() {
    if (topClassLink != null) {
      view.selectClass(topClassLink);
      view.getSubContainer().print("loading...");
      anchorsLoader.load(topClassLink);
    }
}

function selectAnchors() {
    if (topClassLink == null || ! anchorsCache.contains(topClassLink)) {
        return;
    }
    previousQuery = null;
    var condition = query.createAnchorLinkCondition();
    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 openInNewTab(url) {
    window.open(url);
}

function openInClassFrame(url) {
    if (window.parent.frames[2] != null) {
        window.parent.frames[2].location.href = url;
        return true;
    }
}

function showMenu() {
    if (topClassLink == null) {
      return;
    }
    view.selectClass(topClassLink);
    var container = view.getSubContainer();
    var node = container.createContentNode();
    var content;
    if (topClassLink.isPackage()) {
        content = PACKAGE_MENU_PREFERENCE.get();
    } else {
        content = CLASS_MENU_PREFERENCE.get();
    }
    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);
}

/*
 * event handlers
 */
function searchFieldKeyup(e) {
    var code = e.keyCode;
    if (code == 13) {
        returnKeyPressed(e.ctrlKey);
    } else if (code == 27) {
        escapeKeyPressed();
    }
}

function searchFieldChanged(input) {
    query.input(input);
    search();
}

function returnKeyPressed(controlModifier) {
    var url = null;
    if (query.isClassMode() && topClassLink != null) {
        url = topClassLink.getUrl();
    } else if (query.isAnchorMode()) {
        url = topAnchorLink.getUrl();
    }

    if (url != null) {
        if (controlModifier) {
            openInNewTab(url);
        } else {
            openInClassFrame(url);
        }
    }
}

function searchFieldFocus(e) {
    document.body.scrollLeft = 0;
}

function eraseButtonClick() {
    query.erase();
    view.focusField();
    search();
}

function escapeKeyPressed() {
    query.erase();
    search();
}


/**
 * ClassLink
 */
function getHeaderClassLink(html, idx) {
    return new ClassLink("", "", html, idx);
}

function getPackageClassLink(packageName, html, idx) {
    return new ClassLink(packageName, "", html, idx);
}

function getClassLink(packageName, className, html, idx) {
    return new ClassLink(packageName, className, html, idx);
}

function ClassLink(packageName, className, html, idx) {
    this.packageName = packageName;
    this.className = className;
    this.html = html + "<br/>";
    this.url = null;
    if (packageName == "") {
        this.canonicalName = className;
    } else {
        this.canonicalName = packageName + "." + className;
    }
}

ClassLink.prototype.isHeader = function() {
    return this.packageName == "";
}

ClassLink.prototype.isClass = function() {
    return this.className != "";
}

ClassLink.prototype.isPackage = function() {
    return !this.isHeader() && !this.isClass();
}

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;
}

/**
 * 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.createClassLinkCondition = function() {
    var pattern = this.getRegex();

    return function(o) {
        return o.className.match(pattern) || o.canonicalName.match(pattern);
    };
}

Query.prototype.createAnchorLinkCondition = function() {
    var pattern = this.getRegex();

    return function(o) {
        return o.name.match(pattern);
    };
}

Query.prototype.getRegex = function() {
    var q = this.search.replace(/\./g, "\\\.").replace(/\*/g, "\.\*");
    var pattern = "^";
    for (i = 0; i < q.length; i++) {
        var character = q.charAt(i);
        if (/[A-Z]/.test(character)) {
            pattern += "(([a-z]*\.?" + character + ")|" + character.toLowerCase() + ")";
        } else if (/[a-z]/.test(character)) {
            pattern += "(" + character.toUpperCase() + "|" + character + ")";
        } else {
            pattern += character;
        }
    }
    pattern += ".*";
    return pattern;
}

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.erase = function() {
    if (this.isAnchorMode() && 0 < this.search.length) {
        this.update("#");
    } else {
        this.update("");
    }
}

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;
}

/**
 * PreviousQuery
 */
function PreviousQuery(query) {
    this.mode = query.mode;
    this.search = query.search;
}

PreviousQuery.prototype.equals = function(query) {
    return query != null && this.mode == query.mode && this.search == query.search;
}

/**
 * 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);

    var eraseButton = this._createEraseButton();
    node.appendChild(eraseButton);
}

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.setAttribute("type", "text");
    s.addEventListener("keyup", searchFieldKeyup, false);
    s.addEventListener("onchange", searchFieldChanged, false);
    s.addEventListener("focus", searchFieldFocus, false);

    if (SEARCH_ACCESS_KEY != null && SEARCH_ACCESS_KEY != "") {
        s.setAttribute("accesskey", SEARCH_ACCESS_KEY);
    }

    return s;
}

View.prototype._createEraseButton = function() {
    var e = document.createElement("input");
    e.setAttribute("type", "image");
    e.setAttribute("src", ICON_ERASE);
    e.setAttribute("style", "margin-left: 3px");
    e.addEventListener("click", eraseButtonClick, false);

    if (ERASE_ACCESS_KEY != null && ERASE_ACCESS_KEY != "") {
        e.setAttribute("accesskey", ERASE_ACCESS_KEY);
    }

    return e;
}

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 (topAnchorLink != null && AUTO_OPEN_PREFERENCE.get() && ! query.isModeChanged()) {
        var url = topAnchorLink.getUrl();
        if (url != lastAutoOpenUrl) {
            lastAutoOpenUrl = url;
            openInClassFrame(url);
        }
    }
    
    parent.innerHTML = html;
}

/**
 * UserPreference
 */
function UserPreference(key, defaultValue) {
    this.key = key;
    this.defaultValue = defaultValue;
}

UserPreference.prototype.get = function() {
    var value = (GM_getValue) ? GM_getValue(this.key) : undefined;
    if (value == undefined) {
        value = this.defaultValue;
        if (GM_setValue) {
            GM_setValue(this.key, value);
        }
    }
    return value;
}

initialized = init();
watch(view.getFieldElement(), searchFieldChanged);

/*
 * utils
 */
function selectAnyType(xpath) {
    return document.evaluate(xpath, document, null, 
                             XPathResult.ANY_TYPE, null);
}

function watch(element, callback, msec) {
    var elementChanged = false;
    var old;
    setInterval(function(){
        var q = element.value;
        if (elementChanged && old == q) {
            elementChanged = false;
            callback(q);
        } else if (old != q) {
            elementChanged = true;
        }
        old = q;
    }, msec || 200)
}

/*
 * Stopwatch used to log the time taken for the search() function to run.
 */

var startTimeMs = 0;

function logTimeStart() {
  startTimeMs = new Date().getTime();
}

function logTimeStop() {
  if (GM_log && LOG_PREFERENCE.get()) {
    var deltaTimeMs = new Date().getTime() - startTimeMs;
    GM_log("'" + query.getSearchString() + "' in " + deltaTimeMs + "ms\n" + query.getRegex());
  }
}

})();