Commits

Jan Lahoda committed cd61000

Introducing a new improved web frontend

Comments (0)

Files changed (12)

remoting/build.sh

 (cd server/indexer; ant "$@" clean && ant "$@" build-zip && unzip -d ../../build/indexing-backend dist/indexer.zip) || exit 1
 mkdir -p build/indexing-backend/web
 (cd server/web/web.main; ant clean && ant jar && cp -r dist/* ../../../build/indexing-backend/web) || exit 1
+cp -r server/web/web.ui.frontend/public_html build/indexing-backend/web || exit 1
 
 cp server/scripts/* build/indexing-backend
 

remoting/server/scripts/web.sh

 for jar in $DIR/web/lib/*.jar; do
     classpath="$classpath:$jar"
 done
-java $JACKPOT_WEB_OPTS -Djava.index.useMemCache=false -Xbootclasspath/p:$DIR/web/lib/nb-javac-api.jar:$DIR/web/lib/nb-javac-impl.jar -classpath "$classpath" web.main.WebMain "$@"
+java $JACKPOT_WEB_OPTS -Djava.index.useMemCache=false -Xbootclasspath/p:$DIR/web/lib/nb-javac-api.jar:$DIR/web/lib/nb-javac-impl.jar -classpath "$classpath" web.main.WebMain "$@" "$DIR/web/public_html"

remoting/server/web/web.ui.frontend/nbproject/project.properties

+browser.autorefresh.SL__Browsers_ChromeBrowser=true
+browser.highlightselection.SL__Browsers_ChromeBrowser=true
+config.folder=${file.reference.js-frontend-config}
+external.project.url=http://localhost:9998/
+file.reference.js-frontend-config=config
+file.reference.js-frontend-public_html=public_html
+file.reference.js-frontend-test=test
+files.encoding=UTF-8
+server=EXTERNAL
+site.root.folder=${file.reference.js-frontend-public_html}
+start.file=index/ui/index.html
+test.folder=${file.reference.js-frontend-test}
+web.context.root=/js-frontend

remoting/server/web/web.ui.frontend/nbproject/project.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.web.clientproject</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/clientside-project/1">
+            <name>web.ui.frontend</name>
+        </data>
+    </configuration>
+</project>

remoting/server/web/web.ui.frontend/public_html/index/lib/download.xml

+<project name="download" default="download">
+    <target name="download">
+        <property name="imported.basedir" value="${basedir}"/>
+
+        <macrodef name="download">
+             <attribute name="url"/>
+             <attribute name="target"/>
+             <sequential>
+                 <antcall target="-download" inheritAll="false">
+                     <param name="url" value="@{url}" />
+                     <param name="target" value="@{target}" />
+                     <param name="imported.basedir" value="${imported.basedir}" />
+                 </antcall>
+             </sequential>
+        </macrodef>
+
+        <download url="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" target="jquery-1.7.1.min.js"/>
+        <download url="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js" target="jquery-ui-1.8.18.min.js"/>
+        <download url="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/base/jquery-ui.css" target="jquery-ui-1.8.18.css"/>
+        <download url="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.2/css/bootstrap-combined.min.css" target="bootstrap-combined-2.2.2.min.css"/>
+        <download url="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.js" target="angular-1.0.3.js"/>
+        <download url="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.3/underscore-min.js" target="underscore-1.4.3.min.js"/>
+    </target>
+
+    <target name="-download-check-exists">
+        <available property="file.exists" file="${target}"/>
+    </target>
+
+    <target name="-download" depends="-download-check-exists" unless="file.exists">
+        <dirname property="dir" file="${imported.basedir}/${target}" />
+        <mkdir dir="${dir}" />
+        <get src="${url}" dest="${imported.basedir}/${target}" usetimestamp="true"/>
+    </target>
+
+    <target name="-prepare-imported-basedir-property">
+        <dirname property="imported.basedir" file="${ant.file.download}"/>
+    </target>
+    <target name="-post-init" depends="-prepare-imported-basedir-property,download"/>
+
+</project>
Add a comment to this file

remoting/server/web/web.ui.frontend/public_html/index/ui/icons/next.png

Added
New image
Add a comment to this file

remoting/server/web/web.ui.frontend/public_html/index/ui/icons/prev.png

Added
New image

remoting/server/web/web.ui.frontend/public_html/index/ui/index.html

+<!DOCTYPE html>
+<html xmlns:ng="http://angularjs.org" ng-app="ngView">
+<head>
+    <script type="text/javascript" src="/index/lib/jquery-1.7.1.min.js"></script>
+    <script type="text/javascript" src="/index/lib/jquery-ui-1.8.18.min.js"></script>
+    <link href="/index/lib/jquery-ui-1.8.18.css" rel="stylesheet" type="text/css">
+    <link href="/index/lib/bootstrap-combined-2.2.2.min.css" rel="stylesheet">
+    <script src="/index/lib/angular-1.0.3.js"></script>
+    <script src="/index/lib/underscore-1.4.3.min.js"></script>
+    <script src="script.js"></script>
+    <script>
+        angular.module('ngView', [], function($routeProvider, $locationProvider) {
+            $routeProvider.when('/showCode', {
+                templateUrl: 'showCode.html',
+                controller: ShowSourceCode
+            });
+            $routeProvider.when('/search', {
+                templateUrl: 'search.html',
+                controller: DeclarationSearch
+            });
+            $routeProvider.when('/usages', {
+                templateUrl: 'usages.html',
+                controller: UsagesList
+            });
+            $routeProvider.otherwise({
+                templateUrl: 'search.html',
+                controller: DeclarationSearch
+            });
+
+            // configure html5 to get links working on jsfiddle
+//            $locationProvider.html5Mode(true).hashPrefix("!");
+//            $locationProvider.hashPrefix("!");
+        });
+    </script>
+    <style type="text/css">
+        .keyword {color: #0000FF; font-weight: bold;}
+        .comment {color: #737373;}
+        .character {color: #006F00;}
+        .number {color: #780000;}
+        .string {color: #99009D;}
+        .identifier {}
+        .identifier:hover { cursor: hand; cursor: pointer; text-decoration:underline; color:blue;}
+        .whitespace {}
+        .highlight {background-color:#DDDD00;}
+        .constructor {font-weight: bold;}
+        .method {font-weight: bold;}
+        .field {color: #098618; font-weight: bold;}
+        .parameter {color: #a06001;}
+        .unused {color: gray;}
+        .static {font-style:italic;}
+    </style>
+</head>
+    <body ng-controller="topLevel" style="padding: 40px">
+        <div>
+        <div class="navbar navbar-fixed-top">
+            <div class="navbar-inner">
+                <span class="brand">Jackpot 3.0 Code Search</span>
+                <div class="pull-right">
+                    <ul class="nav" ng-show="loading">
+                        <li style="vertical-align: bottom"><a>Loading...</a></li>
+                    </ul>
+                    <ul class="nav"> <!--ng-show="loading">-->
+                        <li style="vertical-align: bottom"><button ng-enable='currentHighlight + 1 < highlights.length' ng-show="showNextPrev" ng-click="nextOccurrence()"><img src="icons/next.png"/></button></li>
+                        <li style="vertical-align: bottom"><button ng-enable='currentHighlight > 0' ng-show="showNextPrev" ng-click="prevOccurrence()"><img src="icons/prev.png"/></button></li>
+                    </ul>
+                    <form ng-submit="globalPerformSearch()" ng-show="showSearch" class="navbar-search pull-right">
+                        <input type="text" class="search-query" name="prefix" ng-model="prefix" placeholder="Search">
+                    </form>
+                </div>
+            </div>
+        </div>
+        <br>
+        <div ng-view></div>
+        </div>
+        <div id="popup"></div>
+    </body>
+</html>

remoting/server/web/web.ui.frontend/public_html/index/ui/script.js

+function DeclarationSearch($scope, $http, $routeParams) {
+    $scope.prefix = "";
+    $scope.performQuery = function() {
+        $scope.$parent.loading = true;
+        $http.get('/index/ui/searchSymbol?prefix=' + $scope.prefix).success(function(data) {
+            var result = [];
+            var index = 0;
+
+            for (var projectId in data) {
+                var projectData = data[projectId].found;
+                var project = $scope.projects.get(projectId);
+
+                for (var relPath in projectData) {
+                    var symbols = projectData[relPath];
+                    for (var j = 0; j < symbols.length; j++) {
+                        symbols[j].project = project;
+                        symbols[j].displayName = symbolDisplayName(symbols[j]).replace("&", "&amp;").replace("<", "&lt;");
+                        symbols[j].enclosingFQN = symbols[j].enclosingFQN.replace("&", "&amp;").replace("<", "&lt;");
+                        result[index++] = symbols[j];
+                    }
+                }
+            }
+
+            $scope.searchResult = result;
+            $scope.$parent.loading = false;
+        });
+    };
+    $scope.performQueryDelayed = _.debounce($scope.performQuery, 2000);
+    $scope.getElementIcon = getElementIcon;
+    $scope.prefix = $routeParams.prefix || "";
+    $scope.symbolSignature = symbolSignature;
+
+    $scope.showSearch = false;
+    $scope.showNextPrev = false;
+    
+    if (typeof $routeParams.prefix !== 'undefined') {
+        $scope.performQuery();
+    }
+}
+
+//Copied from Icons, NetBeans proper
+function getElementIcon(elementKind, modifiers) {
+    var GIF_EXTENSION = ".gif";
+    var PNG_EXTENSION = ".png";
+    if ("PACKAGE" === elementKind) {
+        return "package" + GIF_EXTENSION;
+    } else if ("ENUM" === elementKind) {
+        return "enum" + PNG_EXTENSION;
+    } else if ("ANNOTATION_TYPE" === elementKind) {
+        return "annotation" + PNG_EXTENSION;
+    } else if ("CLASS" === elementKind) {
+        return "class" + PNG_EXTENSION;
+    } else if ("INTERFACE" === elementKind) {
+        return "interface" + PNG_EXTENSION;
+    } else if ("FIELD" === elementKind) {
+        return getIconName("field", PNG_EXTENSION, modifiers);
+    } else if ("ENUM_CONSTANT" === elementKind) {
+        return "constant" + PNG_EXTENSION;
+    } else if ("CONSTRUCTOR" === elementKind) {
+        return getIconName("constructor", PNG_EXTENSION, modifiers);
+    } else if ("INSTANCE_INIT" === elementKind
+            || "STATIC_INIT" === elementKind) {
+        return "initializer" + (modifiers.contains("STATIC") ? "Static" : "") + PNG_EXTENSION;
+    } else if ("METHOD" === elementKind) {
+        return getIconName("method", PNG_EXTENSION, modifiers);
+    } else {
+        return "";
+    }
+}
+
+// Private Methods ---------------------------------------------------------
+
+function getIconName(typeName, extension, modifiers) {
+    var fileName = typeName;
+
+    if (modifiers.indexOf("STATIC") > -1) {
+        fileName += "Static";
+    }
+    if (modifiers.indexOf("PUBLIC") > -1) {
+        fileName += "Public";
+    } else if (modifiers.indexOf("PROTECTED") > -1) {
+        fileName += "Protected";
+    } else if (modifiers.indexOf("PRIVATE") > -1) {
+        fileName += "Private";
+    } else {
+        fileName += "Package";
+    }
+    fileName += extension;
+    return fileName;
+}
+
+function symbolDisplayName(symbol) {
+    if (typeof symbol.kind === undefined) {
+        alert("Undefined kind: " + symbol);
+    }
+    switch (symbol.kind) {
+        case "METHOD":
+        case "CONSTRUCTOR":
+            return "" + symbol.simpleName + decodeMethodSignature(symbol.signature);
+        default:
+            return "" + symbol.simpleName;
+    }
+}
+
+function decodeSignatureType(signature, pos) {
+    var c = signature.charAt(pos[0]++);
+    switch (c) {
+        case 'V':
+            return "void";
+        case 'Z':
+            return "boolean";
+        case 'B':
+            return "byte";
+        case 'S':
+            return "short";
+        case 'I':
+            return "int";
+        case 'J':
+            return "long";
+        case 'C':
+            return "char";
+        case 'F':
+            return "float";
+        case 'D':
+            return "double";
+        case '[':
+            return decodeSignatureType(signature, pos) + "[]";
+        case 'L':
+            {
+                var lastSlash = pos[0];
+                var result = "";
+
+                while (signature.charAt(pos[0]) !== ';' && signature.charAt(pos[0]) !== '<') {
+                    if (signature.charAt(pos[0]) === '/') {
+                        lastSlash = pos[0] + 1;
+                    }
+                    if (signature.charAt(pos[0]) === '$') {
+                        lastSlash = pos[0] + 1;
+                    }
+                    pos[0]++;
+                }
+
+                result += signature.substring(lastSlash, pos[0]);
+
+                if (signature.charAt(pos[0]++) === '<') {
+                    result += '<';
+
+                    while (signature.charAt(pos[0]) !== '>') {
+                        if (result.charAt(result.length - 1) !== '<') {
+                            result += ", ";
+                        }
+                        result += decodeSignatureType(signature, pos);
+                    }
+
+                    result += '>';
+                    pos[0] += 2;
+                }
+
+
+                return result;
+            }
+        case 'T':
+            {
+                var result = "";
+
+                while (signature.charAt(pos[0]) !== ';') {
+                    result += signature.charAt(pos[0]);
+                    pos[0]++;
+                }
+
+                pos[0]++;
+
+                return result;
+            }
+        case '+':
+            return "? extends " + decodeSignatureType(signature, pos);
+        case '-':
+            return "? super " + decodeSignatureType(signature, pos);
+        case '*':
+            return "?";
+        default:
+            return "unknown";
+    }
+}
+function decodeMethodSignature(signature) {
+    var pos = [1];
+
+    if (signature.charAt(0) === '<') {
+        var b = 1;
+
+        while (b > 0) {
+            switch (signature.charAt(pos[0]++)) {
+                case '<':
+                    b++;
+                    break;
+                case '>':
+                    b--;
+                    break;
+            }
+        }
+        ;
+
+        pos[0]++;
+    }
+
+    var result = "(";
+
+    while (signature.charAt(pos[0]) !== ')') {
+        if (result.charAt(result.length - 1) !== '(') {
+            result += ", ";
+        }
+
+        result += decodeSignatureType(signature, pos);
+    }
+
+    result += ')';
+
+    return result;
+}
+
+function symbolSignature(symbolDescription) {
+    var signature;
+
+    switch (symbolDescription.kind) {
+        case "METHOD":
+            signature = "" + symbolDescription.kind + ":" + symbolDescription.enclosingFQN + ":" + symbolDescription.simpleName + ":" + symbolDescription.vmsignature;
+            break;
+        case "CLASS": case "INTERFACE": case "ENUM": case "ANNOTATION_TYPE":
+            signature = "" + symbolDescription.kind + ":" + symbolDescription.fqn;
+            break;
+        default:
+            signature = "" + symbolDescription.kind + ":" + symbolDescription.enclosingFQN + ":" + symbolDescription.simpleName;
+    }
+
+    return signature;
+}
+
+function ShowSourceCode($scope, $http, $routeParams, $location) {
+    $scope.$parent.loading = true;
+
+    $('#popup').dialog('close');
+    $scope.$parent.showSearch = true;
+    $scope.$parent.showNextPrev = true;
+    $scope.$parent.prefix = "";
+
+    var path = $routeParams.path;
+    var relative = $routeParams.relative;
+
+    $scope.gotoAction = function ($event) {
+        $scope.$parent.loading = true;
+        var pos = $($event.target).attr("jpt30pos");
+        var declaration = $($event.target).attr("class").indexOf("declaration") !== (-1);
+        $http.get('/index/ui/target?path=' + path + '&relative=' + relative + '&position=' + pos).success(function(parsedData) {
+            $scope.$parent.loading = false;
+            if (declaration && "signature" in parsedData) {
+                $location.url("/usages?signature=" + escape(parsedData.signature));
+            } else if ("position" in parsedData) {
+                setHash($location, "p" + parsedData.position);
+            } else if ("source" in parsedData) {
+                window.location = "#/showCode?path=" + parsedData.path + "&relative=" + parsedData.source + "&goto=" + parsedData.signature;
+            } else if ("targets" in parsedData) {
+                var popupContent = "The target element is defined in the following files:<br>";
+                popupContent += "<ul>";
+
+                for (var i = 0; i < parsedData.targets.length; i++) {
+                    var categoryData = parsedData.targets[i];
+                    popupContent += "<li>" + categoryData.rootDisplayName/*XXX: escape*/ + "<br>";
+
+                    for (var f = 0; f < categoryData.files.length; f++) {
+                        popupContent += "<img src='/index/icons/javaFile.png' alt='Java File'/>";
+                        popupContent += "<a href='#/showCode?path=" + categoryData.rootPath + "&relative=" + categoryData.files[i] + "&goto=" + parsedData.signature + "'>" + categoryData.files[i] + "</a><br>";
+                    }
+
+                    popupContent += "</li><br>";
+                }
+
+                popupContent += "</ul><br>";
+                $('#popup').html(popupContent)
+                                .dialog({
+                                    title: 'Show',
+                                    width: 800 //XXX: hardcoded size
+                                });
+            } else if ("menu" in parsedData) {
+                var menuDef = parsedData.menu;
+                var popupContent = "";
+                for (var i = 0; i < menuDef.length; i++) {
+                    var menuItem = menuDef[i];
+                    popupContent += '<a href="' + menuItem.url + '">' + menuItem.displayName + '</a><br>';
+                    $('#popup').html(popupContent)
+                                    .dialog({
+                                        title: 'Show',
+                                        width: 800 //XXX: hardcoded size
+                                    });
+                }
+            } else if ("signature" in parsedData) {
+                alert("Cannot find source file for class: " + parsedData.signature.split(":")[1]);
+            } else {
+                alert("Cannot resolve target on this place");
+            }
+        });
+    };
+
+    $http.get("/index/source/cat?path=" + escape(path) + "&relative=" + escape(relative)).success(function(data) {
+        $scope.sourceCode = data.replace(/\r\n/g, '\n');
+        $http.get("/index/ui/highlightData?path=" + escape(path) + "&relative=" + escape(relative)).success(function(parsedData) {
+            doColoring(path, relative, parsedData.categories, parsedData.spans, $scope, $location, $routeParams, $http);
+        });
+    });
+    
+    $scope.browsedFile = relative;
+    $scope.$parent.currentHighlight = 0;
+    $scope.$parent.highlights = [];
+
+    $scope.$parent.nextOccurrence = function() {
+        setHash($location, "p" + $scope.$parent.highlights[++$scope.currentHighlight][0]);
+    };
+
+    $scope.$parent.prevOccurrence = function() {
+        setHash($location, "p" + $scope.$parent.highlights[--$scope.currentHighlight][0]);
+    };
+}
+
+function doColoring(path, relative, $highlights, $spans, $scope, $location, $routeParams, $http) {
+    var $code = $scope.sourceCode;
+    var $coloredCode = "";
+
+    var $current = 0;
+    for (var i = 0; i < $highlights.length; i++ ) {
+        $coloredCode += '<span id="p' + $current + '" class="' + $highlights[i] + '"' + ($highlights[i].indexOf("identifier") !== (-1) ? ' jpt30pos="' + $current + '"' : '') + '>' + $code.slice($current, $current+$spans[i]).replace(/&/g, '&amp;').replace(/</g, '&lt;') + "</span>";
+        $current += $spans[i];
+    }
+
+    $scope.sourceCode = $coloredCode;
+
+    $location.replace();
+
+    if (!$location.hash()) {
+        var goto = $routeParams.goto;
+
+        if (goto) {
+            $http.get('/index/ui/declarationSpan?path=' + path + '&relative=' + relative + '&signature=' + unescape(goto)).success(function(parsedData) {
+                if (parsedData[2] !== (-1)) {
+                    setHash($location, "p" + parsedData[2]);
+                }
+            });
+        }
+    } else {
+        fixScrollingCrap($location.hash());
+    }
+
+    if (!$routeParams.goto && $routeParams.highlights) {
+        $http.get($routeParams.highlights).success(function(parsedData) {
+            $(".highlight").removeClass("highlight");
+
+            for (var i = 0; i < parsedData.length; i++) {
+                var start = parsedData[i][0];
+
+                $("#p" + start).addClass("highlight");
+            }
+
+            if (parsedData.length > 0) {
+                $scope.$parent.currentHighlight = -1;
+                $scope.$parent.highlights = parsedData;
+                $scope.$parent.nextOccurrence();
+            } else {
+                $scope.$parent.currentHighlight = 0;
+                $scope.$parent.highlights = [];
+            }
+        });
+    }
+
+    $scope.$parent.loading = false;
+}
+
+function setHash($location, newHash) {
+//TODO: set a bookmarkable location without refreshing the route?
+//    $location.hash(newHash);
+    fixScrollingCrap(newHash);
+}
+
+function fixScrollingCrap(scrollToHash) {
+    var whereToScroll = $("#" + scrollToHash);
+    $('html, body').scrollTop(whereToScroll.offset().top - 50);
+}
+
+function topLevel($scope, $route, $routeParams, $location, $http) {
+    $scope.$route = $route;
+    $scope.$location = $location;
+    $scope.$routeParams = $routeParams;
+
+    $scope.globalPerformSearch = function() {
+        $location.url("/search?prefix=" + $scope.prefix);
+    };
+
+    $http.get("/index/list").success(function(data) {
+        var result = [];
+        var lines = data.split('\n');
+        for (var i = 0; i < lines.length; i++) {
+            if (lines[i].length === 0) continue;
+            var colon = lines[i].indexOf(':');
+            var c = new Object();
+            c.id = lines[i].substring(0, colon);
+            c.displayName = lines[i].substring(colon + 1);
+            c.selected = true;
+            result[i] = c;
+        }
+        result.sort(function(l, r) { return l.displayName < r.displayName ? -1 : l.displayName > r.displayName ? 1 : 0;});
+        result.get = function(id) {
+            for (var i = 0; i < result.length; i++) {
+                if (result[i].id === id) return result[i];
+            }
+        };
+        $scope.projects = result;
+        $scope.projectCheckBoxChanged();
+    });
+    $scope.projects = [];
+    $scope.allProjectsCheckBoxChanged = function() {
+        for (var i = 0; i < $scope.projects.length; i++) {
+            $scope.projects[i].selected = $scope.allProjectsCheckBox;
+        }
+    };
+    $scope.projectCheckBoxChanged = function() {
+        var checked = true;
+        for (var i = 0; i < $scope.projects.length; i++) {
+            checked = checked && $scope.projects[i].selected;
+        }
+        $scope.allProjectsCheckBox = checked;
+    };
+}
+
+function UsagesList($scope, $route, $routeParams, $location, $http) {
+    var signature = $routeParams.signature;
+    $http.get("/index/ui/searchUsages?signature=" + escape($routeParams.signature)).success(function(data) {
+        var result = [];
+        var index = 0;
+
+        for (var projectId in data) {
+            var projectData = data[projectId];
+            var project = $scope.projects.get(projectId);
+
+            for (var file in projectData) {
+                var usageDescription = projectData[file];
+                usageDescription.file = file;
+                usageDescription.project = project;
+                result[index++] = usageDescription;
+            }
+        }
+        
+        result.sort(function(l, r) { return l.file.localeCompare(r.file); });
+        result.sort(function(l, r) { return l.project.displayName.localeCompare(r.project.displayName); });
+
+        $scope.usages = result;
+    });
+
+    var signatureParts = signature.split(":");
+
+    switch (signatureParts[0]) {
+        case "METHOD":
+        case "CONSTRUCTOR":
+            $scope.symbolDisplayName = "" + signatureParts[2] + decodeMethodSignature(signatureParts[3]) + " in " + signatureParts[1];
+            break;
+        case "FIELD":
+        case "ENUM_CONSTANT":
+            $scope.symbolDisplayName = "" + signatureParts[2] + " in " + signatureParts[1];
+            break;
+        default:
+            $scope.symbolDisplayName = "" + signatureParts[1];
+            break;
+    }
+
+    $scope.signatureKind = signatureParts[0];
+    $scope.signature = signature;
+    $scope.escape = escape;
+    $scope.showUsages = true;
+    $scope.showSubtypes = true;
+    
+    $scope.showSearch = true;
+    $scope.showNextPrev = false;
+}

remoting/server/web/web.ui.frontend/public_html/index/ui/search.html

+<form ng-submit="performQuery()">
+    <input class="search-query" ng-model="prefix" type="text" name="prefix" placeholder="Search"/><!--ng-change="performQueryDelayed()" -->
+    <button class="btn" type="submit">Search</button>
+</form>
+<label>Projects:</label>
+<input ng-model="$parent.allProjectsCheckBox" ng-change="$parent.allProjectsCheckBoxChanged()" type="checkbox" checked="yes">All</input>
+<span class="project-checkboxes" ng-repeat="project in $parent.projects">
+    <input class="projectCheckBox" type='checkbox' ng-model="project.selected" name="path" ng-change="$parent.projectCheckBoxChanged()">{{project.displayName}}</input>
+</span>
+<table width="100%">
+    <tr ng-repeat="symbolsDescription in searchResult" ng-show="projects.get(symbolsDescription.project.id).selected==true"> <!--projects[symbolsDescription.projectId].neco==true">-->
+        <td style="vertical-align: top">
+            <img src="/index/icons/{{getElementIcon(symbolsDescription.kind, symbolsDescription.modifiers)}}" alt="{{symbolsDescription.kind}}"/>
+        </td><td>
+            <a href="#/showCode?path={{symbolsDescription.project.id}}&relative={{symbolsDescription.file}}&goto={{symbolSignature(symbolsDescription)}}">{{symbolsDescription.displayName}}</a> in {{symbolsDescription.enclosingFQN}}
+        </td>
+        <td  align="right">{{symbolsDescription.project.displayName.replace("&", "&amp;").replace("<", "&lt;").replace(" ", "&nbsp;")}}</td>
+    </tr>
+</table>

remoting/server/web/web.ui.frontend/public_html/index/ui/showCode.html

+<pre id="code" ng-bind-html-unsafe="sourceCode" ng-click="gotoAction($event)">
+</pre>
+<div class="navbar navbar-fixed-bottom">
+    <div class="navbar-inner">
+        <ul class="nav">
+            <li style="vertical-align: bottom"><a>{{browsedFile}}</a></li>
+        </ul>
+    </div>
+</div>

remoting/server/web/web.ui.frontend/public_html/index/ui/usages.html

+References to {{symbolDisplayName}} found in the following files:
+<br>
+<label>Projects:</label>
+<input ng-model="$parent.allProjectsCheckBox" ng-change="$parent.allProjectsCheckBoxChanged()" type="checkbox" checked="yes">All</input>
+<span class="project-checkboxes" ng-repeat="project in $parent.projects">
+    <input class="projectCheckBox" type='checkbox' ng-model="project.selected" name="path" ng-change="$parent.projectCheckBoxChanged()">{{project.displayName}}</input>
+</span>
+<br><br>
+<input type='checkbox' ng-model="showUsages">Usages</input>
+<input type='checkbox' ng-model="showSubtypes">Subtypes/Overriders</input>
+<br><br>
+<table width="100%">
+    <tr ng-repeat="usage in usages" ng-show="projects.get(usage.project.id).selected==true && ((showUsages && !!usage.usage) || (showSubtypes && (usage.subtypes || usage.overridersParents)))">
+        <td style="vertical-align: top">
+            <img src="/index/icons/{{getElementIcon(symbolsDescription.kind, symbolsDescription.modifiers)}}" alt="{{symbolsDescription.kind}}"/>
+        </td><td>
+            <a href="#/showCode?path={{usage.project.id}}&relative={{usage.file}}&highlights={{escape('/index/ui/localUsages?path=' + usage.project.id + '&relative=' + usage.file + '&signature=' + signature)}}">{{usage.file}}</a>
+        </td>
+        <td  align="right">{{usage.project.displayName.replace("&", "&amp;").replace("<", "&lt;").replace(" ", "&nbsp;")}}</td>
+    </tr>
+</table>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.