Introducing a new improved web frontend
authorJan Lahoda <jlahoda@netbeans.org>
Sun, 10 Feb 2013 21:53:29 +0100
changeset 93059b4764e8101
parent 929 57dc42a0fcd6
child 931 c4498e0cd934
Introducing a new improved web frontend
remoting/build.sh
remoting/server/scripts/web.sh
remoting/server/web/web.ui.frontend/nbproject/project.properties
remoting/server/web/web.ui.frontend/nbproject/project.xml
remoting/server/web/web.ui.frontend/public_html/index/lib/download.xml
remoting/server/web/web.ui.frontend/public_html/index/ui/icons/next.png
remoting/server/web/web.ui.frontend/public_html/index/ui/icons/prev.png
remoting/server/web/web.ui.frontend/public_html/index/ui/index.html
remoting/server/web/web.ui.frontend/public_html/index/ui/script.js
remoting/server/web/web.ui.frontend/public_html/index/ui/search.html
remoting/server/web/web.ui.frontend/public_html/index/ui/showCode.html
remoting/server/web/web.ui.frontend/public_html/index/ui/usages.html
     1.1 --- a/remoting/build.sh	Sun Feb 10 15:00:16 2013 +0100
     1.2 +++ b/remoting/build.sh	Sun Feb 10 21:53:29 2013 +0100
     1.3 @@ -6,6 +6,7 @@
     1.4  (cd server/indexer; ant "$@" clean && ant "$@" build-zip && unzip -d ../../build/indexing-backend dist/indexer.zip) || exit 1
     1.5  mkdir -p build/indexing-backend/web
     1.6  (cd server/web/web.main; ant clean && ant jar && cp -r dist/* ../../../build/indexing-backend/web) || exit 1
     1.7 +cp -r server/web/web.ui.frontend/public_html build/indexing-backend/web || exit 1
     1.8  
     1.9  cp server/scripts/* build/indexing-backend
    1.10  
     2.1 --- a/remoting/server/scripts/web.sh	Sun Feb 10 15:00:16 2013 +0100
     2.2 +++ b/remoting/server/scripts/web.sh	Sun Feb 10 21:53:29 2013 +0100
     2.3 @@ -5,4 +5,4 @@
     2.4  for jar in $DIR/web/lib/*.jar; do
     2.5      classpath="$classpath:$jar"
     2.6  done
     2.7 -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 "$@"
     2.8 +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"
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/remoting/server/web/web.ui.frontend/nbproject/project.properties	Sun Feb 10 21:53:29 2013 +0100
     3.3 @@ -0,0 +1,13 @@
     3.4 +browser.autorefresh.SL__Browsers_ChromeBrowser=true
     3.5 +browser.highlightselection.SL__Browsers_ChromeBrowser=true
     3.6 +config.folder=${file.reference.js-frontend-config}
     3.7 +external.project.url=http://localhost:9998/
     3.8 +file.reference.js-frontend-config=config
     3.9 +file.reference.js-frontend-public_html=public_html
    3.10 +file.reference.js-frontend-test=test
    3.11 +files.encoding=UTF-8
    3.12 +server=EXTERNAL
    3.13 +site.root.folder=${file.reference.js-frontend-public_html}
    3.14 +start.file=index/ui/index.html
    3.15 +test.folder=${file.reference.js-frontend-test}
    3.16 +web.context.root=/js-frontend
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/remoting/server/web/web.ui.frontend/nbproject/project.xml	Sun Feb 10 21:53:29 2013 +0100
     4.3 @@ -0,0 +1,9 @@
     4.4 +<?xml version="1.0" encoding="UTF-8"?>
     4.5 +<project xmlns="http://www.netbeans.org/ns/project/1">
     4.6 +    <type>org.netbeans.modules.web.clientproject</type>
     4.7 +    <configuration>
     4.8 +        <data xmlns="http://www.netbeans.org/ns/clientside-project/1">
     4.9 +            <name>web.ui.frontend</name>
    4.10 +        </data>
    4.11 +    </configuration>
    4.12 +</project>
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/remoting/server/web/web.ui.frontend/public_html/index/lib/download.xml	Sun Feb 10 21:53:29 2013 +0100
     5.3 @@ -0,0 +1,40 @@
     5.4 +<project name="download" default="download">
     5.5 +    <target name="download">
     5.6 +        <property name="imported.basedir" value="${basedir}"/>
     5.7 +
     5.8 +        <macrodef name="download">
     5.9 +             <attribute name="url"/>
    5.10 +             <attribute name="target"/>
    5.11 +             <sequential>
    5.12 +                 <antcall target="-download" inheritAll="false">
    5.13 +                     <param name="url" value="@{url}" />
    5.14 +                     <param name="target" value="@{target}" />
    5.15 +                     <param name="imported.basedir" value="${imported.basedir}" />
    5.16 +                 </antcall>
    5.17 +             </sequential>
    5.18 +        </macrodef>
    5.19 +
    5.20 +        <download url="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" target="jquery-1.7.1.min.js"/>
    5.21 +        <download url="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js" target="jquery-ui-1.8.18.min.js"/>
    5.22 +        <download url="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/base/jquery-ui.css" target="jquery-ui-1.8.18.css"/>
    5.23 +        <download url="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.2/css/bootstrap-combined.min.css" target="bootstrap-combined-2.2.2.min.css"/>
    5.24 +        <download url="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.js" target="angular-1.0.3.js"/>
    5.25 +        <download url="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.3/underscore-min.js" target="underscore-1.4.3.min.js"/>
    5.26 +    </target>
    5.27 +
    5.28 +    <target name="-download-check-exists">
    5.29 +        <available property="file.exists" file="${target}"/>
    5.30 +    </target>
    5.31 +
    5.32 +    <target name="-download" depends="-download-check-exists" unless="file.exists">
    5.33 +        <dirname property="dir" file="${imported.basedir}/${target}" />
    5.34 +        <mkdir dir="${dir}" />
    5.35 +        <get src="${url}" dest="${imported.basedir}/${target}" usetimestamp="true"/>
    5.36 +    </target>
    5.37 +
    5.38 +    <target name="-prepare-imported-basedir-property">
    5.39 +        <dirname property="imported.basedir" file="${ant.file.download}"/>
    5.40 +    </target>
    5.41 +    <target name="-post-init" depends="-prepare-imported-basedir-property,download"/>
    5.42 +
    5.43 +</project>
     6.1 Binary file remoting/server/web/web.ui.frontend/public_html/index/ui/icons/next.png has changed
     7.1 Binary file remoting/server/web/web.ui.frontend/public_html/index/ui/icons/prev.png has changed
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/remoting/server/web/web.ui.frontend/public_html/index/ui/index.html	Sun Feb 10 21:53:29 2013 +0100
     8.3 @@ -0,0 +1,77 @@
     8.4 +<!DOCTYPE html>
     8.5 +<html xmlns:ng="http://angularjs.org" ng-app="ngView">
     8.6 +<head>
     8.7 +    <script type="text/javascript" src="/index/lib/jquery-1.7.1.min.js"></script>
     8.8 +    <script type="text/javascript" src="/index/lib/jquery-ui-1.8.18.min.js"></script>
     8.9 +    <link href="/index/lib/jquery-ui-1.8.18.css" rel="stylesheet" type="text/css">
    8.10 +    <link href="/index/lib/bootstrap-combined-2.2.2.min.css" rel="stylesheet">
    8.11 +    <script src="/index/lib/angular-1.0.3.js"></script>
    8.12 +    <script src="/index/lib/underscore-1.4.3.min.js"></script>
    8.13 +    <script src="script.js"></script>
    8.14 +    <script>
    8.15 +        angular.module('ngView', [], function($routeProvider, $locationProvider) {
    8.16 +            $routeProvider.when('/showCode', {
    8.17 +                templateUrl: 'showCode.html',
    8.18 +                controller: ShowSourceCode
    8.19 +            });
    8.20 +            $routeProvider.when('/search', {
    8.21 +                templateUrl: 'search.html',
    8.22 +                controller: DeclarationSearch
    8.23 +            });
    8.24 +            $routeProvider.when('/usages', {
    8.25 +                templateUrl: 'usages.html',
    8.26 +                controller: UsagesList
    8.27 +            });
    8.28 +            $routeProvider.otherwise({
    8.29 +                templateUrl: 'search.html',
    8.30 +                controller: DeclarationSearch
    8.31 +            });
    8.32 +
    8.33 +            // configure html5 to get links working on jsfiddle
    8.34 +//            $locationProvider.html5Mode(true).hashPrefix("!");
    8.35 +//            $locationProvider.hashPrefix("!");
    8.36 +        });
    8.37 +    </script>
    8.38 +    <style type="text/css">
    8.39 +        .keyword {color: #0000FF; font-weight: bold;}
    8.40 +        .comment {color: #737373;}
    8.41 +        .character {color: #006F00;}
    8.42 +        .number {color: #780000;}
    8.43 +        .string {color: #99009D;}
    8.44 +        .identifier {}
    8.45 +        .identifier:hover { cursor: hand; cursor: pointer; text-decoration:underline; color:blue;}
    8.46 +        .whitespace {}
    8.47 +        .highlight {background-color:#DDDD00;}
    8.48 +        .constructor {font-weight: bold;}
    8.49 +        .method {font-weight: bold;}
    8.50 +        .field {color: #098618; font-weight: bold;}
    8.51 +        .parameter {color: #a06001;}
    8.52 +        .unused {color: gray;}
    8.53 +        .static {font-style:italic;}
    8.54 +    </style>
    8.55 +</head>
    8.56 +    <body ng-controller="topLevel" style="padding: 40px">
    8.57 +        <div>
    8.58 +        <div class="navbar navbar-fixed-top">
    8.59 +            <div class="navbar-inner">
    8.60 +                <span class="brand">Jackpot 3.0 Code Search</span>
    8.61 +                <div class="pull-right">
    8.62 +                    <ul class="nav" ng-show="loading">
    8.63 +                        <li style="vertical-align: bottom"><a>Loading...</a></li>
    8.64 +                    </ul>
    8.65 +                    <ul class="nav"> <!--ng-show="loading">-->
    8.66 +                        <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>
    8.67 +                        <li style="vertical-align: bottom"><button ng-enable='currentHighlight > 0' ng-show="showNextPrev" ng-click="prevOccurrence()"><img src="icons/prev.png"/></button></li>
    8.68 +                    </ul>
    8.69 +                    <form ng-submit="globalPerformSearch()" ng-show="showSearch" class="navbar-search pull-right">
    8.70 +                        <input type="text" class="search-query" name="prefix" ng-model="prefix" placeholder="Search">
    8.71 +                    </form>
    8.72 +                </div>
    8.73 +            </div>
    8.74 +        </div>
    8.75 +        <br>
    8.76 +        <div ng-view></div>
    8.77 +        </div>
    8.78 +        <div id="popup"></div>
    8.79 +    </body>
    8.80 +</html>
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/remoting/server/web/web.ui.frontend/public_html/index/ui/script.js	Sun Feb 10 21:53:29 2013 +0100
     9.3 @@ -0,0 +1,479 @@
     9.4 +function DeclarationSearch($scope, $http, $routeParams) {
     9.5 +    $scope.prefix = "";
     9.6 +    $scope.performQuery = function() {
     9.7 +        $scope.$parent.loading = true;
     9.8 +        $http.get('/index/ui/searchSymbol?prefix=' + $scope.prefix).success(function(data) {
     9.9 +            var result = [];
    9.10 +            var index = 0;
    9.11 +
    9.12 +            for (var projectId in data) {
    9.13 +                var projectData = data[projectId].found;
    9.14 +                var project = $scope.projects.get(projectId);
    9.15 +
    9.16 +                for (var relPath in projectData) {
    9.17 +                    var symbols = projectData[relPath];
    9.18 +                    for (var j = 0; j < symbols.length; j++) {
    9.19 +                        symbols[j].project = project;
    9.20 +                        symbols[j].displayName = symbolDisplayName(symbols[j]).replace("&", "&amp;").replace("<", "&lt;");
    9.21 +                        symbols[j].enclosingFQN = symbols[j].enclosingFQN.replace("&", "&amp;").replace("<", "&lt;");
    9.22 +                        result[index++] = symbols[j];
    9.23 +                    }
    9.24 +                }
    9.25 +            }
    9.26 +
    9.27 +            $scope.searchResult = result;
    9.28 +            $scope.$parent.loading = false;
    9.29 +        });
    9.30 +    };
    9.31 +    $scope.performQueryDelayed = _.debounce($scope.performQuery, 2000);
    9.32 +    $scope.getElementIcon = getElementIcon;
    9.33 +    $scope.prefix = $routeParams.prefix || "";
    9.34 +    $scope.symbolSignature = symbolSignature;
    9.35 +
    9.36 +    $scope.showSearch = false;
    9.37 +    $scope.showNextPrev = false;
    9.38 +    
    9.39 +    if (typeof $routeParams.prefix !== 'undefined') {
    9.40 +        $scope.performQuery();
    9.41 +    }
    9.42 +}
    9.43 +
    9.44 +//Copied from Icons, NetBeans proper
    9.45 +function getElementIcon(elementKind, modifiers) {
    9.46 +    var GIF_EXTENSION = ".gif";
    9.47 +    var PNG_EXTENSION = ".png";
    9.48 +    if ("PACKAGE" === elementKind) {
    9.49 +        return "package" + GIF_EXTENSION;
    9.50 +    } else if ("ENUM" === elementKind) {
    9.51 +        return "enum" + PNG_EXTENSION;
    9.52 +    } else if ("ANNOTATION_TYPE" === elementKind) {
    9.53 +        return "annotation" + PNG_EXTENSION;
    9.54 +    } else if ("CLASS" === elementKind) {
    9.55 +        return "class" + PNG_EXTENSION;
    9.56 +    } else if ("INTERFACE" === elementKind) {
    9.57 +        return "interface" + PNG_EXTENSION;
    9.58 +    } else if ("FIELD" === elementKind) {
    9.59 +        return getIconName("field", PNG_EXTENSION, modifiers);
    9.60 +    } else if ("ENUM_CONSTANT" === elementKind) {
    9.61 +        return "constant" + PNG_EXTENSION;
    9.62 +    } else if ("CONSTRUCTOR" === elementKind) {
    9.63 +        return getIconName("constructor", PNG_EXTENSION, modifiers);
    9.64 +    } else if ("INSTANCE_INIT" === elementKind
    9.65 +            || "STATIC_INIT" === elementKind) {
    9.66 +        return "initializer" + (modifiers.contains("STATIC") ? "Static" : "") + PNG_EXTENSION;
    9.67 +    } else if ("METHOD" === elementKind) {
    9.68 +        return getIconName("method", PNG_EXTENSION, modifiers);
    9.69 +    } else {
    9.70 +        return "";
    9.71 +    }
    9.72 +}
    9.73 +
    9.74 +// Private Methods ---------------------------------------------------------
    9.75 +
    9.76 +function getIconName(typeName, extension, modifiers) {
    9.77 +    var fileName = typeName;
    9.78 +
    9.79 +    if (modifiers.indexOf("STATIC") > -1) {
    9.80 +        fileName += "Static";
    9.81 +    }
    9.82 +    if (modifiers.indexOf("PUBLIC") > -1) {
    9.83 +        fileName += "Public";
    9.84 +    } else if (modifiers.indexOf("PROTECTED") > -1) {
    9.85 +        fileName += "Protected";
    9.86 +    } else if (modifiers.indexOf("PRIVATE") > -1) {
    9.87 +        fileName += "Private";
    9.88 +    } else {
    9.89 +        fileName += "Package";
    9.90 +    }
    9.91 +    fileName += extension;
    9.92 +    return fileName;
    9.93 +}
    9.94 +
    9.95 +function symbolDisplayName(symbol) {
    9.96 +    if (typeof symbol.kind === undefined) {
    9.97 +        alert("Undefined kind: " + symbol);
    9.98 +    }
    9.99 +    switch (symbol.kind) {
   9.100 +        case "METHOD":
   9.101 +        case "CONSTRUCTOR":
   9.102 +            return "" + symbol.simpleName + decodeMethodSignature(symbol.signature);
   9.103 +        default:
   9.104 +            return "" + symbol.simpleName;
   9.105 +    }
   9.106 +}
   9.107 +
   9.108 +function decodeSignatureType(signature, pos) {
   9.109 +    var c = signature.charAt(pos[0]++);
   9.110 +    switch (c) {
   9.111 +        case 'V':
   9.112 +            return "void";
   9.113 +        case 'Z':
   9.114 +            return "boolean";
   9.115 +        case 'B':
   9.116 +            return "byte";
   9.117 +        case 'S':
   9.118 +            return "short";
   9.119 +        case 'I':
   9.120 +            return "int";
   9.121 +        case 'J':
   9.122 +            return "long";
   9.123 +        case 'C':
   9.124 +            return "char";
   9.125 +        case 'F':
   9.126 +            return "float";
   9.127 +        case 'D':
   9.128 +            return "double";
   9.129 +        case '[':
   9.130 +            return decodeSignatureType(signature, pos) + "[]";
   9.131 +        case 'L':
   9.132 +            {
   9.133 +                var lastSlash = pos[0];
   9.134 +                var result = "";
   9.135 +
   9.136 +                while (signature.charAt(pos[0]) !== ';' && signature.charAt(pos[0]) !== '<') {
   9.137 +                    if (signature.charAt(pos[0]) === '/') {
   9.138 +                        lastSlash = pos[0] + 1;
   9.139 +                    }
   9.140 +                    if (signature.charAt(pos[0]) === '$') {
   9.141 +                        lastSlash = pos[0] + 1;
   9.142 +                    }
   9.143 +                    pos[0]++;
   9.144 +                }
   9.145 +
   9.146 +                result += signature.substring(lastSlash, pos[0]);
   9.147 +
   9.148 +                if (signature.charAt(pos[0]++) === '<') {
   9.149 +                    result += '<';
   9.150 +
   9.151 +                    while (signature.charAt(pos[0]) !== '>') {
   9.152 +                        if (result.charAt(result.length - 1) !== '<') {
   9.153 +                            result += ", ";
   9.154 +                        }
   9.155 +                        result += decodeSignatureType(signature, pos);
   9.156 +                    }
   9.157 +
   9.158 +                    result += '>';
   9.159 +                    pos[0] += 2;
   9.160 +                }
   9.161 +
   9.162 +
   9.163 +                return result;
   9.164 +            }
   9.165 +        case 'T':
   9.166 +            {
   9.167 +                var result = "";
   9.168 +
   9.169 +                while (signature.charAt(pos[0]) !== ';') {
   9.170 +                    result += signature.charAt(pos[0]);
   9.171 +                    pos[0]++;
   9.172 +                }
   9.173 +
   9.174 +                pos[0]++;
   9.175 +
   9.176 +                return result;
   9.177 +            }
   9.178 +        case '+':
   9.179 +            return "? extends " + decodeSignatureType(signature, pos);
   9.180 +        case '-':
   9.181 +            return "? super " + decodeSignatureType(signature, pos);
   9.182 +        case '*':
   9.183 +            return "?";
   9.184 +        default:
   9.185 +            return "unknown";
   9.186 +    }
   9.187 +}
   9.188 +function decodeMethodSignature(signature) {
   9.189 +    var pos = [1];
   9.190 +
   9.191 +    if (signature.charAt(0) === '<') {
   9.192 +        var b = 1;
   9.193 +
   9.194 +        while (b > 0) {
   9.195 +            switch (signature.charAt(pos[0]++)) {
   9.196 +                case '<':
   9.197 +                    b++;
   9.198 +                    break;
   9.199 +                case '>':
   9.200 +                    b--;
   9.201 +                    break;
   9.202 +            }
   9.203 +        }
   9.204 +        ;
   9.205 +
   9.206 +        pos[0]++;
   9.207 +    }
   9.208 +
   9.209 +    var result = "(";
   9.210 +
   9.211 +    while (signature.charAt(pos[0]) !== ')') {
   9.212 +        if (result.charAt(result.length - 1) !== '(') {
   9.213 +            result += ", ";
   9.214 +        }
   9.215 +
   9.216 +        result += decodeSignatureType(signature, pos);
   9.217 +    }
   9.218 +
   9.219 +    result += ')';
   9.220 +
   9.221 +    return result;
   9.222 +}
   9.223 +
   9.224 +function symbolSignature(symbolDescription) {
   9.225 +    var signature;
   9.226 +
   9.227 +    switch (symbolDescription.kind) {
   9.228 +        case "METHOD":
   9.229 +            signature = "" + symbolDescription.kind + ":" + symbolDescription.enclosingFQN + ":" + symbolDescription.simpleName + ":" + symbolDescription.vmsignature;
   9.230 +            break;
   9.231 +        case "CLASS": case "INTERFACE": case "ENUM": case "ANNOTATION_TYPE":
   9.232 +            signature = "" + symbolDescription.kind + ":" + symbolDescription.fqn;
   9.233 +            break;
   9.234 +        default:
   9.235 +            signature = "" + symbolDescription.kind + ":" + symbolDescription.enclosingFQN + ":" + symbolDescription.simpleName;
   9.236 +    }
   9.237 +
   9.238 +    return signature;
   9.239 +}
   9.240 +
   9.241 +function ShowSourceCode($scope, $http, $routeParams, $location) {
   9.242 +    $scope.$parent.loading = true;
   9.243 +
   9.244 +    $('#popup').dialog('close');
   9.245 +    $scope.$parent.showSearch = true;
   9.246 +    $scope.$parent.showNextPrev = true;
   9.247 +    $scope.$parent.prefix = "";
   9.248 +
   9.249 +    var path = $routeParams.path;
   9.250 +    var relative = $routeParams.relative;
   9.251 +
   9.252 +    $scope.gotoAction = function ($event) {
   9.253 +        $scope.$parent.loading = true;
   9.254 +        var pos = $($event.target).attr("jpt30pos");
   9.255 +        var declaration = $($event.target).attr("class").indexOf("declaration") !== (-1);
   9.256 +        $http.get('/index/ui/target?path=' + path + '&relative=' + relative + '&position=' + pos).success(function(parsedData) {
   9.257 +            $scope.$parent.loading = false;
   9.258 +            if (declaration && "signature" in parsedData) {
   9.259 +                $location.url("/usages?signature=" + escape(parsedData.signature));
   9.260 +            } else if ("position" in parsedData) {
   9.261 +                setHash($location, "p" + parsedData.position);
   9.262 +            } else if ("source" in parsedData) {
   9.263 +                window.location = "#/showCode?path=" + parsedData.path + "&relative=" + parsedData.source + "&goto=" + parsedData.signature;
   9.264 +            } else if ("targets" in parsedData) {
   9.265 +                var popupContent = "The target element is defined in the following files:<br>";
   9.266 +                popupContent += "<ul>";
   9.267 +
   9.268 +                for (var i = 0; i < parsedData.targets.length; i++) {
   9.269 +                    var categoryData = parsedData.targets[i];
   9.270 +                    popupContent += "<li>" + categoryData.rootDisplayName/*XXX: escape*/ + "<br>";
   9.271 +
   9.272 +                    for (var f = 0; f < categoryData.files.length; f++) {
   9.273 +                        popupContent += "<img src='/index/icons/javaFile.png' alt='Java File'/>";
   9.274 +                        popupContent += "<a href='#/showCode?path=" + categoryData.rootPath + "&relative=" + categoryData.files[i] + "&goto=" + parsedData.signature + "'>" + categoryData.files[i] + "</a><br>";
   9.275 +                    }
   9.276 +
   9.277 +                    popupContent += "</li><br>";
   9.278 +                }
   9.279 +
   9.280 +                popupContent += "</ul><br>";
   9.281 +                $('#popup').html(popupContent)
   9.282 +                                .dialog({
   9.283 +                                    title: 'Show',
   9.284 +                                    width: 800 //XXX: hardcoded size
   9.285 +                                });
   9.286 +            } else if ("menu" in parsedData) {
   9.287 +                var menuDef = parsedData.menu;
   9.288 +                var popupContent = "";
   9.289 +                for (var i = 0; i < menuDef.length; i++) {
   9.290 +                    var menuItem = menuDef[i];
   9.291 +                    popupContent += '<a href="' + menuItem.url + '">' + menuItem.displayName + '</a><br>';
   9.292 +                    $('#popup').html(popupContent)
   9.293 +                                    .dialog({
   9.294 +                                        title: 'Show',
   9.295 +                                        width: 800 //XXX: hardcoded size
   9.296 +                                    });
   9.297 +                }
   9.298 +            } else if ("signature" in parsedData) {
   9.299 +                alert("Cannot find source file for class: " + parsedData.signature.split(":")[1]);
   9.300 +            } else {
   9.301 +                alert("Cannot resolve target on this place");
   9.302 +            }
   9.303 +        });
   9.304 +    };
   9.305 +
   9.306 +    $http.get("/index/source/cat?path=" + escape(path) + "&relative=" + escape(relative)).success(function(data) {
   9.307 +        $scope.sourceCode = data.replace(/\r\n/g, '\n');
   9.308 +        $http.get("/index/ui/highlightData?path=" + escape(path) + "&relative=" + escape(relative)).success(function(parsedData) {
   9.309 +            doColoring(path, relative, parsedData.categories, parsedData.spans, $scope, $location, $routeParams, $http);
   9.310 +        });
   9.311 +    });
   9.312 +    
   9.313 +    $scope.browsedFile = relative;
   9.314 +    $scope.$parent.currentHighlight = 0;
   9.315 +    $scope.$parent.highlights = [];
   9.316 +
   9.317 +    $scope.$parent.nextOccurrence = function() {
   9.318 +        setHash($location, "p" + $scope.$parent.highlights[++$scope.currentHighlight][0]);
   9.319 +    };
   9.320 +
   9.321 +    $scope.$parent.prevOccurrence = function() {
   9.322 +        setHash($location, "p" + $scope.$parent.highlights[--$scope.currentHighlight][0]);
   9.323 +    };
   9.324 +}
   9.325 +
   9.326 +function doColoring(path, relative, $highlights, $spans, $scope, $location, $routeParams, $http) {
   9.327 +    var $code = $scope.sourceCode;
   9.328 +    var $coloredCode = "";
   9.329 +
   9.330 +    var $current = 0;
   9.331 +    for (var i = 0; i < $highlights.length; i++ ) {
   9.332 +        $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>";
   9.333 +        $current += $spans[i];
   9.334 +    }
   9.335 +
   9.336 +    $scope.sourceCode = $coloredCode;
   9.337 +
   9.338 +    $location.replace();
   9.339 +
   9.340 +    if (!$location.hash()) {
   9.341 +        var goto = $routeParams.goto;
   9.342 +
   9.343 +        if (goto) {
   9.344 +            $http.get('/index/ui/declarationSpan?path=' + path + '&relative=' + relative + '&signature=' + unescape(goto)).success(function(parsedData) {
   9.345 +                if (parsedData[2] !== (-1)) {
   9.346 +                    setHash($location, "p" + parsedData[2]);
   9.347 +                }
   9.348 +            });
   9.349 +        }
   9.350 +    } else {
   9.351 +        fixScrollingCrap($location.hash());
   9.352 +    }
   9.353 +
   9.354 +    if (!$routeParams.goto && $routeParams.highlights) {
   9.355 +        $http.get($routeParams.highlights).success(function(parsedData) {
   9.356 +            $(".highlight").removeClass("highlight");
   9.357 +
   9.358 +            for (var i = 0; i < parsedData.length; i++) {
   9.359 +                var start = parsedData[i][0];
   9.360 +
   9.361 +                $("#p" + start).addClass("highlight");
   9.362 +            }
   9.363 +
   9.364 +            if (parsedData.length > 0) {
   9.365 +                $scope.$parent.currentHighlight = -1;
   9.366 +                $scope.$parent.highlights = parsedData;
   9.367 +                $scope.$parent.nextOccurrence();
   9.368 +            } else {
   9.369 +                $scope.$parent.currentHighlight = 0;
   9.370 +                $scope.$parent.highlights = [];
   9.371 +            }
   9.372 +        });
   9.373 +    }
   9.374 +
   9.375 +    $scope.$parent.loading = false;
   9.376 +}
   9.377 +
   9.378 +function setHash($location, newHash) {
   9.379 +//TODO: set a bookmarkable location without refreshing the route?
   9.380 +//    $location.hash(newHash);
   9.381 +    fixScrollingCrap(newHash);
   9.382 +}
   9.383 +
   9.384 +function fixScrollingCrap(scrollToHash) {
   9.385 +    var whereToScroll = $("#" + scrollToHash);
   9.386 +    $('html, body').scrollTop(whereToScroll.offset().top - 50);
   9.387 +}
   9.388 +
   9.389 +function topLevel($scope, $route, $routeParams, $location, $http) {
   9.390 +    $scope.$route = $route;
   9.391 +    $scope.$location = $location;
   9.392 +    $scope.$routeParams = $routeParams;
   9.393 +
   9.394 +    $scope.globalPerformSearch = function() {
   9.395 +        $location.url("/search?prefix=" + $scope.prefix);
   9.396 +    };
   9.397 +
   9.398 +    $http.get("/index/list").success(function(data) {
   9.399 +        var result = [];
   9.400 +        var lines = data.split('\n');
   9.401 +        for (var i = 0; i < lines.length; i++) {
   9.402 +            if (lines[i].length === 0) continue;
   9.403 +            var colon = lines[i].indexOf(':');
   9.404 +            var c = new Object();
   9.405 +            c.id = lines[i].substring(0, colon);
   9.406 +            c.displayName = lines[i].substring(colon + 1);
   9.407 +            c.selected = true;
   9.408 +            result[i] = c;
   9.409 +        }
   9.410 +        result.sort(function(l, r) { return l.displayName < r.displayName ? -1 : l.displayName > r.displayName ? 1 : 0;});
   9.411 +        result.get = function(id) {
   9.412 +            for (var i = 0; i < result.length; i++) {
   9.413 +                if (result[i].id === id) return result[i];
   9.414 +            }
   9.415 +        };
   9.416 +        $scope.projects = result;
   9.417 +        $scope.projectCheckBoxChanged();
   9.418 +    });
   9.419 +    $scope.projects = [];
   9.420 +    $scope.allProjectsCheckBoxChanged = function() {
   9.421 +        for (var i = 0; i < $scope.projects.length; i++) {
   9.422 +            $scope.projects[i].selected = $scope.allProjectsCheckBox;
   9.423 +        }
   9.424 +    };
   9.425 +    $scope.projectCheckBoxChanged = function() {
   9.426 +        var checked = true;
   9.427 +        for (var i = 0; i < $scope.projects.length; i++) {
   9.428 +            checked = checked && $scope.projects[i].selected;
   9.429 +        }
   9.430 +        $scope.allProjectsCheckBox = checked;
   9.431 +    };
   9.432 +}
   9.433 +
   9.434 +function UsagesList($scope, $route, $routeParams, $location, $http) {
   9.435 +    var signature = $routeParams.signature;
   9.436 +    $http.get("/index/ui/searchUsages?signature=" + escape($routeParams.signature)).success(function(data) {
   9.437 +        var result = [];
   9.438 +        var index = 0;
   9.439 +
   9.440 +        for (var projectId in data) {
   9.441 +            var projectData = data[projectId];
   9.442 +            var project = $scope.projects.get(projectId);
   9.443 +
   9.444 +            for (var file in projectData) {
   9.445 +                var usageDescription = projectData[file];
   9.446 +                usageDescription.file = file;
   9.447 +                usageDescription.project = project;
   9.448 +                result[index++] = usageDescription;
   9.449 +            }
   9.450 +        }
   9.451 +        
   9.452 +        result.sort(function(l, r) { return l.file.localeCompare(r.file); });
   9.453 +        result.sort(function(l, r) { return l.project.displayName.localeCompare(r.project.displayName); });
   9.454 +
   9.455 +        $scope.usages = result;
   9.456 +    });
   9.457 +
   9.458 +    var signatureParts = signature.split(":");
   9.459 +
   9.460 +    switch (signatureParts[0]) {
   9.461 +        case "METHOD":
   9.462 +        case "CONSTRUCTOR":
   9.463 +            $scope.symbolDisplayName = "" + signatureParts[2] + decodeMethodSignature(signatureParts[3]) + " in " + signatureParts[1];
   9.464 +            break;
   9.465 +        case "FIELD":
   9.466 +        case "ENUM_CONSTANT":
   9.467 +            $scope.symbolDisplayName = "" + signatureParts[2] + " in " + signatureParts[1];
   9.468 +            break;
   9.469 +        default:
   9.470 +            $scope.symbolDisplayName = "" + signatureParts[1];
   9.471 +            break;
   9.472 +    }
   9.473 +
   9.474 +    $scope.signatureKind = signatureParts[0];
   9.475 +    $scope.signature = signature;
   9.476 +    $scope.escape = escape;
   9.477 +    $scope.showUsages = true;
   9.478 +    $scope.showSubtypes = true;
   9.479 +    
   9.480 +    $scope.showSearch = true;
   9.481 +    $scope.showNextPrev = false;
   9.482 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/remoting/server/web/web.ui.frontend/public_html/index/ui/search.html	Sun Feb 10 21:53:29 2013 +0100
    10.3 @@ -0,0 +1,19 @@
    10.4 +<form ng-submit="performQuery()">
    10.5 +    <input class="search-query" ng-model="prefix" type="text" name="prefix" placeholder="Search"/><!--ng-change="performQueryDelayed()" -->
    10.6 +    <button class="btn" type="submit">Search</button>
    10.7 +</form>
    10.8 +<label>Projects:</label>
    10.9 +<input ng-model="$parent.allProjectsCheckBox" ng-change="$parent.allProjectsCheckBoxChanged()" type="checkbox" checked="yes">All</input>
   10.10 +<span class="project-checkboxes" ng-repeat="project in $parent.projects">
   10.11 +    <input class="projectCheckBox" type='checkbox' ng-model="project.selected" name="path" ng-change="$parent.projectCheckBoxChanged()">{{project.displayName}}</input>
   10.12 +</span>
   10.13 +<table width="100%">
   10.14 +    <tr ng-repeat="symbolsDescription in searchResult" ng-show="projects.get(symbolsDescription.project.id).selected==true"> <!--projects[symbolsDescription.projectId].neco==true">-->
   10.15 +        <td style="vertical-align: top">
   10.16 +            <img src="/index/icons/{{getElementIcon(symbolsDescription.kind, symbolsDescription.modifiers)}}" alt="{{symbolsDescription.kind}}"/>
   10.17 +        </td><td>
   10.18 +            <a href="#/showCode?path={{symbolsDescription.project.id}}&relative={{symbolsDescription.file}}&goto={{symbolSignature(symbolsDescription)}}">{{symbolsDescription.displayName}}</a> in {{symbolsDescription.enclosingFQN}}
   10.19 +        </td>
   10.20 +        <td  align="right">{{symbolsDescription.project.displayName.replace("&", "&amp;").replace("<", "&lt;").replace(" ", "&nbsp;")}}</td>
   10.21 +    </tr>
   10.22 +</table>
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/remoting/server/web/web.ui.frontend/public_html/index/ui/showCode.html	Sun Feb 10 21:53:29 2013 +0100
    11.3 @@ -0,0 +1,9 @@
    11.4 +<pre id="code" ng-bind-html-unsafe="sourceCode" ng-click="gotoAction($event)">
    11.5 +</pre>
    11.6 +<div class="navbar navbar-fixed-bottom">
    11.7 +    <div class="navbar-inner">
    11.8 +        <ul class="nav">
    11.9 +            <li style="vertical-align: bottom"><a>{{browsedFile}}</a></li>
   11.10 +        </ul>
   11.11 +    </div>
   11.12 +</div>
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/remoting/server/web/web.ui.frontend/public_html/index/ui/usages.html	Sun Feb 10 21:53:29 2013 +0100
    12.3 @@ -0,0 +1,21 @@
    12.4 +References to {{symbolDisplayName}} found in the following files:
    12.5 +<br>
    12.6 +<label>Projects:</label>
    12.7 +<input ng-model="$parent.allProjectsCheckBox" ng-change="$parent.allProjectsCheckBoxChanged()" type="checkbox" checked="yes">All</input>
    12.8 +<span class="project-checkboxes" ng-repeat="project in $parent.projects">
    12.9 +    <input class="projectCheckBox" type='checkbox' ng-model="project.selected" name="path" ng-change="$parent.projectCheckBoxChanged()">{{project.displayName}}</input>
   12.10 +</span>
   12.11 +<br><br>
   12.12 +<input type='checkbox' ng-model="showUsages">Usages</input>
   12.13 +<input type='checkbox' ng-model="showSubtypes">Subtypes/Overriders</input>
   12.14 +<br><br>
   12.15 +<table width="100%">
   12.16 +    <tr ng-repeat="usage in usages" ng-show="projects.get(usage.project.id).selected==true && ((showUsages && !!usage.usage) || (showSubtypes && (usage.subtypes || usage.overridersParents)))">
   12.17 +        <td style="vertical-align: top">
   12.18 +            <img src="/index/icons/{{getElementIcon(symbolsDescription.kind, symbolsDescription.modifiers)}}" alt="{{symbolsDescription.kind}}"/>
   12.19 +        </td><td>
   12.20 +            <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>
   12.21 +        </td>
   12.22 +        <td  align="right">{{usage.project.displayName.replace("&", "&amp;").replace("<", "&lt;").replace(" ", "&nbsp;")}}</td>
   12.23 +    </tr>
   12.24 +</table>