diff --git a/.project b/.project
new file mode 100644
index 0000000000000000000000000000000000000000..4c665a9634680143a83ebefc8924cc653f32ec40
--- /dev/null
+++ b/.project
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>vipra-config</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+	</buildSpec>
+	<natures>
+	</natures>
+</projectDescription>
diff --git a/vipra-cmd/src/main/resources/log4j2.out.xml b/vipra-cmd/src/main/resources/log4j2.out.xml
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/vipra-ui/app/html/about.html b/vipra-ui/app/html/about.html
index ea3d126ed77be79200725345e05ea4c38356d63a..edd025ffe06df95478efccaa5c932c837f3ae770 100644
--- a/vipra-ui/app/html/about.html
+++ b/vipra-ui/app/html/about.html
@@ -261,6 +261,4 @@
       </tr>
     </tbody>
   </table>
-</div>
-
-<div ui-view ng-cloak></div>
\ No newline at end of file
+</div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/articles/index.html b/vipra-ui/app/html/articles/index.html
index 9cee6b1cec0ca6b302c17066db11670bb05a6dab..103bfb96843f75b14e4cf03b0e073e50997fe6f5 100644
--- a/vipra-ui/app/html/articles/index.html
+++ b/vipra-ui/app/html/articles/index.html
@@ -1,5 +1,5 @@
 <div ng-cloak>
-  <div class="text-muted list-header">
+  <div class="text-muted">
     Found <ng-pluralize count="articlesTotal||0" when="{0:'no articles',1:'1 article',other:'{} articles'}"></ng-pluralize> in the database.
     <span ng-show="articlesTotal">
       Sort by
@@ -18,6 +18,8 @@
     Page <span ng-bind="page||1"></span> of <span ng-bind="maxPage||1"></span>.
   </div>
 
+  <pagination total="articlesTotal" page="page" limit="limit" change="changePage"/>
+
   <ol ng-attr-start="{{(page-1)*limit+1}}">
     <li ng-repeat="article in articles">
       <a ui-sref="articles.show({id: article.id})" ng-bind="::article.title"></a>
@@ -25,6 +27,4 @@
   </ol>
 
   <pagination total="articlesTotal" page="page" limit="limit" change="changePage"/>
-</div>
-
-<div ui-view ng-cloak></div>
\ No newline at end of file
+</div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/articles/show.html b/vipra-ui/app/html/articles/show.html
index be469f5e7d003b9f8d1572fe949cccad7b718f6e..27afa34735a1fe8eba0d27a8b4f4c62782560564 100644
--- a/vipra-ui/app/html/articles/show.html
+++ b/vipra-ui/app/html/articles/show.html
@@ -53,11 +53,11 @@
     </div>
   </div>
 
-  <h3>Topics <span ng-bind-template="({{article.topics.length}})"></span> <hide-link target="#topics"/></h3>
+  <h3>Topics <span ng-bind-template="({{article.topics.length}})" ng-show="article.topics.length > 0"></span> <hide-link target="#topics"/></h3>
 
   <div class="row" id="topics">
-    <div class="col-md-5 topic-links">
-      <table class="table table-morecondensed">
+    <div class="col-md-5">
+      <table class="table table-morecondensed" ng-show="article.topics.length > 0">
         <tr>
           <th sort-by="topic.topic.name" sort-type="topicSort" sort-reverse="topicSortRev">
             Name
@@ -73,6 +73,7 @@
           <td class="text-right" ng-bind-template="{{topic.share}}%"></td>
         </tr>
       </table>
+      <span class="text-muted" ng-hide="article.topics.length > 0">No topics</span>
     </div>
     <div class="col-md-7">
       <div class="pie-chart" id="topic-share" highcharts="topicShare"></div>
@@ -81,6 +82,4 @@
 
   <hr>
   <p ng-bind-html="::article.text" class="text-justify"></p>
-</div>
-
-<div ui-view ng-cloak></div>
\ No newline at end of file
+</div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/directives/pagination.html b/vipra-ui/app/html/directives/pagination.html
index 7bd65a8d8a54188480f8f1a5ec9097c637174f61..2f38a4f4d0a71f0f767863b13a47328dcdbcb6b6 100644
--- a/vipra-ui/app/html/directives/pagination.html
+++ b/vipra-ui/app/html/directives/pagination.html
@@ -1,15 +1,28 @@
-<nav ng-show="total > limit">
+<nav>
   <ul class="pagination">
-    <li ng-class="{disabled:page==1}">
-      <a ui-sref="{page:page==2?null:page-1}" ng-show="page>1" ng-click="changePage(page-1)">&laquo;</a>
+    <li title="First" ng-class="{disabled:page==1}">
+      <a ui-sref="{page:page==2?null:page-1}" ng-show="page>1" ng-click="changePage(1)">&laquo;</a>
       <span ng-hide="page>1">&laquo;</span>
     </li>
+    <li title="Previous" ng-class="{disabled:page==1}">
+      <a ui-sref="{page:page==2?null:page-1}" ng-show="page>1" ng-click="changePage(page-1)">&lsaquo;</a>
+      <span ng-hide="page>1">&lsaquo;</span>
+    </li>
     <li ng-class="{active:p==page}" ng-repeat="p in pages">
       <a ui-sref="{page:p===1?null:p}" ng-bind="p" ng-click="changePage(p)"></a>
     </li>
-    <li ng-class="{disabled:page>=maxPage}">
-      <a ui-sref="{page:page+1}" ng-show="page<maxPage" ng-click="changePage(page+1)">&raquo;</a>
+    <li title="Next" ng-class="{disabled:page>=maxPage}">
+      <a ui-sref="{page:page+1}" ng-show="page<maxPage" ng-click="changePage(page+1)">&rsaquo;</a>
+      <span ng-hide="page<maxPage">&rsaquo;</span>
+    </li>
+    <li title="Last" ng-class="{disabled:page>=maxPage}">
+      <a ui-sref="{page:page+1}" ng-show="page<maxPage" ng-click="changePage(maxPage)">&raquo;</a>
       <span ng-hide="page<maxPage">&raquo;</span>
     </li>
   </ul>
+  <ul class="pagination" ng-show="total > limit">
+    <li>
+      <a ng-click="toPage()">Page...</a>
+    </li>
+  </ul>
 </nav>
\ No newline at end of file
diff --git a/vipra-ui/app/html/network.html b/vipra-ui/app/html/network.html
index 2ee3327a7e20ac8a46dbf177735693b2ba03fc88..b05ce3981550f677285fce56df92b31528175799 100644
--- a/vipra-ui/app/html/network.html
+++ b/vipra-ui/app/html/network.html
@@ -13,6 +13,4 @@
     </div>
     <div class="fullsize navpadding" id="visgraph"></div>
   </div>
-</div>
-
-<div ui-view ng-cloak></div>
\ No newline at end of file
+</div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/topics/articles.html b/vipra-ui/app/html/topics/articles.html
deleted file mode 100644
index 6151b621ce3dbe07904273c2360f91df03a4b43a..0000000000000000000000000000000000000000
--- a/vipra-ui/app/html/topics/articles.html
+++ /dev/null
@@ -1,61 +0,0 @@
-<div ng-cloak>
-  <div class="page-header">
-    <h1>
-      <div ng-bind="topic.name" ng-hide="isRename"></div>
-      <div class="input-group input-group-lg" ng-show="isRename">
-        <input type="text" class="form-control" ng-model="topic.name" id="topicName" ng-keyup="keyup($event)">
-        <div class="input-group-btn">
-          <button class="btn btn-success" ng-click="endRename(true)">
-            <span class="glyphicon glyphicon-ok"></span>
-          </button>
-          <button class="btn btn-danger" ng-click="endRename(false)">
-            <span class="glyphicon glyphicon-remove"></span>
-          </button>
-        </div>
-      </div>
-    </h1>
-
-    <bs-alert type="danger" ng-if="renameErrors">
-      <span ng-bind-html="renameErrors"></span>
-    </bs-alert>
-    
-    <table class="item-actions">
-      <tr>
-        <td>
-          <a class="btn btn-default" ui-sref="topics.show({id:topic.id})">Back</a>
-        </td>
-      </tr>
-    </table>
-  </div>
-
-  <h3>Articles</h3>
-
-  <div class="text-muted list-header">
-    Found <ng-pluralize count="articlesTotal||0" when="{0:'no articles',1:'1 article',other:'{} articles'}"></ng-pluralize> in the database.
-    <span ng-show="articlesTotal">
-      Sort by
-      <ol class="nya-bs-select nya-bs-condensed" ng-model="sort">
-        <li value="title" class="nya-bs-option"><a>Title</a></li>
-        <li value="date" class="nya-bs-option"><a>Date</a></li>
-        <li value="created" class="nya-bs-option"><a>Added</a></li>
-      </ol>
-      Direction
-      <ol class="nya-bs-select nya-bs-condensed" ng-model="order">
-        <li value="+" class="nya-bs-option"><a>Ascending</a></li>
-        <li value="-" class="nya-bs-option"><a>Descending</a></li>
-      </ol>
-    </span>
-    <br>
-    Page <span ng-bind="page||1"></span> of <span ng-bind="maxPage||1"></span>.
-  </div>
-
-  <ol ng-attr-start="{{(page-1)*limit+1}}">
-    <li ng-repeat="article in articles">
-      <a ui-sref="articles.show({id: article.id})" ng-bind="::article.title"></a>
-    </li>
-  </ol>
-
-  <pagination total="articlesTotal" page="page" limit="limit" change="changePage"/>
-</div>
-
-<div ui-view ng-cloak></div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/topics/index.html b/vipra-ui/app/html/topics/index.html
index 5b12c06af43c55ceef402045e0ba197b71f983ea..8d5a249654d34abc170ce3b082260889ad84a9b0 100644
--- a/vipra-ui/app/html/topics/index.html
+++ b/vipra-ui/app/html/topics/index.html
@@ -1,5 +1,5 @@
 <div ng-cloak>
-  <div class="text-muted list-header">
+  <div class="text-muted">
     Found <ng-pluralize count="topicsTotal||0" when="{0:'no topics',1:'1 topic',other:'{} topics'}"></ng-pluralize> in the database.
     <span ng-show="topicsTotal">
       Sort by
@@ -17,6 +17,8 @@
     Page <span ng-bind="page||1"></span> of <span ng-bind="maxPage||1"></span>.
   </div>
 
+  <pagination total="topicsTotal" page="page" limit="limit" change="changePage"/>
+
   <ol ng-attr-start="{{(page-1)*limit+1}}">
     <li ng-repeat="topic in topics">
       <a ui-sref="topics.show({id: topic.id})">{{topic.name}}</a>
@@ -24,6 +26,4 @@
   </ol>
 
   <pagination total="topicsTotal" page="page" limit="limit" change="changePage"/>
-</div>
-
-<div ui-view ng-cloak></div>
\ No newline at end of file
+</div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/topics/show.html b/vipra-ui/app/html/topics/show.html
index 24a75e58ea999ca4f7b6fa53657323baba659d2f..964d974382856a7a1d3d1c6d998e644e76daf2ec 100644
--- a/vipra-ui/app/html/topics/show.html
+++ b/vipra-ui/app/html/topics/show.html
@@ -29,9 +29,6 @@
         <td>
           <a class="btn btn-default" ui-sref="network({type:'topics', id:topic.id})">Network graph</a>
         </td>
-        <td>
-          <a class="btn btn-default" ui-sref="topics.show.articles({id:topic.id})">Articles</a>
-        </td>
       </tr>
     </table>
   </div>
@@ -87,6 +84,4 @@
       </table>
     </div>
   </div>
-</div>
-
-<div ui-view ng-cloak></div>
\ No newline at end of file
+</div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/words/index.html b/vipra-ui/app/html/words/index.html
index 3a8b497da307a21c67b40de23e70c58ca1da034e..5da54c2da1f787f3e4e2ed9686bc80fc63c52923 100644
--- a/vipra-ui/app/html/words/index.html
+++ b/vipra-ui/app/html/words/index.html
@@ -1,5 +1,5 @@
 <div ng-cloak>
-  <div class="text-muted list-header">
+  <div class="text-muted">
     Found <ng-pluralize count="wordsTotal||0" when="{0:'no words',1:'1 word',other:'{} words'}"></ng-pluralize> in the database.
     <span ng-show="wordsTotal">
       Sort by
@@ -17,6 +17,8 @@
     Page <span ng-bind="page||1"></span> of <span ng-bind="maxPage||1"></span>.
   </div>
 
+  <pagination total="wordsTotal" page="page" limit="limit" change="changePage"/>
+
   <div class="row">
     <div class="col-md-4">
       <ul class="list-unstyled">
@@ -42,6 +44,4 @@
   </div>
 
   <pagination total="wordsTotal" page="page" limit="limit" change="changePage"/>
-</div>
-
-<div ui-view ng-cloak></div>
\ No newline at end of file
+</div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/words/show.html b/vipra-ui/app/html/words/show.html
index 7745702e67bd856f3f49fe000ef6a575fd622ca0..891b8f3d74e1bf00e2fa551ca1024aa3b633accd 100644
--- a/vipra-ui/app/html/words/show.html
+++ b/vipra-ui/app/html/words/show.html
@@ -22,13 +22,11 @@
 
   <div class="row" id="topics">
     <div class="col-md-12">
-      <ul class="dashed">
+      <ol>
         <li ng-repeat="topic in ::topics">
           <topic-link topic="topic"/>
         </li>
-      </ul>
+      </ol>
     </div>
   </div>
-</div>
-
-<div ui-view ng-cloak></div>
\ No newline at end of file
+</div>
\ No newline at end of file
diff --git a/vipra-ui/app/js/app.js b/vipra-ui/app/js/app.js
index ad8233a300a5827026acbccacafb9b6ba2de7f84..bfb10682a41c91bacb933c2e6ac5c6d34ac1efaf 100644
--- a/vipra-ui/app/js/app.js
+++ b/vipra-ui/app/js/app.js
@@ -22,7 +22,7 @@
     $urlRouterProvider.otherwise('/');
 
     // states
-
+    
     $stateProvider.state('index', {
       url: '/',
       templateUrl: Vipra.const.tplBase + '/index.html',
@@ -51,8 +51,12 @@
 
     $stateProvider.state('articles.show', {
       url: '/:id',
-      templateUrl: Vipra.const.tplBase + '/articles/show.html',
-      controller: 'ArticlesShowController'
+      views: {
+        "@": {
+          templateUrl: Vipra.const.tplBase + '/articles/show.html',
+          controller: 'ArticlesShowController'
+        }
+      }
     });
 
     // states: topics
@@ -65,14 +69,12 @@
 
     $stateProvider.state('topics.show', {
       url: '/:id',
-      templateUrl: Vipra.const.tplBase + '/topics/show.html',
-      controller: 'TopicsShowController'
-    });
-
-    $stateProvider.state('topics.show.articles', {
-      url: '/articles',
-      templateUrl: Vipra.const.tplBase + '/topics/articles.html',
-      controller: 'TopicsArticlesController'
+      "views": {
+        "@": {
+          templateUrl: Vipra.const.tplBase + '/topics/show.html',
+          controller: 'TopicsShowController'
+        }
+      }
     });
 
     // states: words
@@ -85,8 +87,12 @@
 
     $stateProvider.state('words.show', {
       url: '/:id',
-      templateUrl: Vipra.const.tplBase + '/words/show.html',
-      controller: 'WordsShowController'
+      "views": {
+        "@": {
+          templateUrl: Vipra.const.tplBase + '/words/show.html',
+          controller: 'WordsShowController'
+        }
+      }
     });
 
   }]);
@@ -147,6 +153,11 @@
     $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
       console.log('changing state: ' + toState.name);
       NProgress.start();
+
+      loadingTimeout = setTimeout(function() {
+        console.log('loading done');
+        NProgress.done();
+      }, 100);
     });
 
   }]);
diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js
index 7e948452d243745f9a081576826f10f742cb42e9..5fa9f2e55136d44d10ea17369a97697399aa06c9 100644
--- a/vipra-ui/app/js/controllers.js
+++ b/vipra-ui/app/js/controllers.js
@@ -219,9 +219,11 @@
           if(node.type === 'article' && $scope.shown.topics) {
             // node is article, load article to get topics
             ArticleFactory.get({id:node.dbid}, function(data) {
-              for(var i = 0; i < data.topics.length; i++)
-                data.topics[i] = data.topics[i].topic;
-              constructor(data.topics, node, topicNode);
+              if(data.topics) {
+                for(var i = 0; i < data.topics.length; i++)
+                  data.topics[i] = data.topics[i].topic;
+                constructor(data.topics, node, topicNode);
+              }
             });
           } else if(node.type === 'topic') {
             // node is topic, load topic to get words and articles
@@ -301,34 +303,36 @@
       $scope.articleModified = Vipra.formatDateTime($scope.article.modified);
 
       // calculate percentage share
-      var topicShareSeries = [],
-          topics = $scope.article.topics;
-          topicsCount = 0;
-      for(var i = 0; i < topics.length; i++)
-        topicsCount += topics[i].count;
-      for(var i = 0; i < topics.length; i++) {
-        var share = Vipra.toPercent(topics[i].count / topicsCount);
-        topics[i].share = share;
-        topicShareSeries.push({name: topics[i].topic.name.ellipsize(20), y: share});
-      }
-
-      // highcharts data
-      var topicShare = {
-        chart: { type: 'pie' },
-        credits: { enabled: false },
-        plotOptions: {
-          pie: { allowPointSelect: true }
-        },
-        title: { text: '' },
-        tooltip: { pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>' },
-        series: [{
-          name: 'Topic Share',
-          colorByPoint: true,
-          data: topicShareSeries
-        }]
-      };
+      if($scope.article.topics) {
+        var topicShareSeries = [],
+            topics = $scope.article.topics;
+            topicsCount = 0;
+        for(var i = 0; i < topics.length; i++)
+          topicsCount += topics[i].count;
+        for(var i = 0; i < topics.length; i++) {
+          var share = Vipra.toPercent(topics[i].count / topicsCount);
+          topics[i].share = share;
+          topicShareSeries.push({name: topics[i].topic.name.ellipsize(20), y: share});
+        }
 
-      $scope.topicShare = topicShare;
+        // highcharts data
+        var topicShare = {
+          chart: { type: 'pie' },
+          credits: { enabled: false },
+          plotOptions: {
+            pie: { allowPointSelect: true }
+          },
+          title: { text: '' },
+          tooltip: { pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>' },
+          series: [{
+            name: 'Topic Share',
+            colorByPoint: true,
+            data: topicShareSeries
+          }]
+        };
+
+        $scope.topicShare = topicShare;
+      }
     });
 
   }]);
@@ -534,6 +538,12 @@
         change(page);
         window.scrollTo(0,0);
       };
+
+      $scope.toPage = function() {
+        var page = prompt("Enter a page number (between 1 and " + $scope.maxPage + ")");
+        if(page > 0 && page <= $scope.maxPage)
+          $scope.changePage(page);
+      };
    }]);
 
 })();
\ No newline at end of file
diff --git a/vipra-ui/app/less/app.less b/vipra-ui/app/less/app.less
index 8ce3fe41c6f75c17628eac5e3b8a7e5f8e3fb541..da98522ab4bc21f5643e493bded0c5ec7b589b85 100644
--- a/vipra-ui/app/less/app.less
+++ b/vipra-ui/app/less/app.less
@@ -7,8 +7,6 @@ html {
 
 body {
   padding-bottom: 20px;
-  /* Margin bottom by footer height */
-  margin-bottom: 60px;
 }
 
 a:hover {
@@ -247,10 +245,6 @@ ul.dashed {
   margin-top: -2px;
 }
 
-.list-header {
-  padding-bottom: 10px;
-}
-
 revolve-select, [revolve-select] {
   .noselect;
   cursor: pointer;
diff --git a/vipra-ui/bower.json b/vipra-ui/bower.json
index 5389febe402d9676daf3db23635ca6421396c3c2..02a607dfe4a39d53e7e20146dd11c1846c966cba 100644
--- a/vipra-ui/bower.json
+++ b/vipra-ui/bower.json
@@ -24,10 +24,10 @@
     "angular-ui-router": "^0.2.17",
     "angular-sanitize": "^1.5.0",
     "highcharts": "^4.2.2",
-    "nprogress": "^0.2.0",
     "vis": "https://github.com/almende/vis.git#^4.14.0",
     "angular-websocket": "^1.0.14",
     "moment": "^2.11.2",
-    "nya-bootstrap-select": "^2.1.3"
+    "nya-bootstrap-select": "^2.1.3",
+    "nprogress": "^0.2.0"
   }
 }
diff --git a/vipra-util/pom.xml b/vipra-util/pom.xml
index aa784ea6b69595efbbcc077bb504b5dafbceb5b6..1519155c68e20a808dc96b534fccb4d80535bde8 100644
--- a/vipra-util/pom.xml
+++ b/vipra-util/pom.xml
@@ -11,7 +11,7 @@
 		<maven.compiler.target>1.8</maven.compiler.target>
 		<maven.compiler.source>1.8</maven.compiler.source>
 		<maven.build.timestamp.format>yyMMdd_HHmm</maven.build.timestamp.format>
-   		<buildDate>${maven.build.timestamp}</buildDate>
+		<buildDate>${maven.build.timestamp}</buildDate>
 	</properties>
 
 	<scm>
diff --git a/vipra-util/src/main/java/de/vipra/util/service/Service.java b/vipra-util/src/main/java/de/vipra/util/service/Service.java
index df287617c27a17ec64a1d254da21e588fd4622ab..3e9aff75b8cd10cf9d73e01c854463d3c1c23cd7 100644
--- a/vipra-util/src/main/java/de/vipra/util/service/Service.java
+++ b/vipra-util/src/main/java/de/vipra/util/service/Service.java
@@ -264,6 +264,8 @@ public interface Service<Type extends Model<IdType>, IdType, E extends Exception
 		}
 
 		public String getSortBy() {
+			if (sortBy == null)
+				return null;
 			return sortBy.startsWith("+") ? sortBy.substring(1) : sortBy;
 		}