diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java new file mode 100644 index 0000000000000000000000000000000000000000..18dde950fac14ac1fba9931fb718af5809580280 --- /dev/null +++ b/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java @@ -0,0 +1,5 @@ +package de.vipra.rest.resource; + +public class InfoResource { + +} diff --git a/vipra-ui/app/html/topics/articles.html b/vipra-ui/app/html/topics/articles.html new file mode 100644 index 0000000000000000000000000000000000000000..7b188562b7717c706b8fb32c0e04b886e0cd51bd --- /dev/null +++ b/vipra-ui/app/html/topics/articles.html @@ -0,0 +1,38 @@ +<div ui-view 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> + <bs-dropdown label="Actions"> + <li><a ng-click="startRename()">Rename</a></li> + </bs-dropdown> + </td> + <td> + <a class="btn btn-default" ui-sref="network({type:'topics', id:topic.id})">Network graph</a> + </td> + </tr> + </table> + </div> + + <h3>Info <hide-link target="#info"/></h3> + +</div> \ No newline at end of file diff --git a/vipra-ui/app/js/app.js b/vipra-ui/app/js/app.js index f88787b5c56818d6834f7a5114c2f18bf09a841c..d44403e71662ce72062bd7f3489e5cbc8e28c345 100644 --- a/vipra-ui/app/js/app.js +++ b/vipra-ui/app/js/app.js @@ -18,21 +18,19 @@ app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) { - var tplBase = 'html'; - $urlRouterProvider.otherwise('/'); // states $stateProvider.state('index', { url: '/', - templateUrl: tplBase + '/index.html', + templateUrl: Vipra.const.tplBase + '/index.html', controller: 'IndexController' }); $stateProvider.state('network', { url: '/network/:type/:id', - templateUrl: tplBase + '/network.html', + templateUrl: Vipra.const.tplBase + '/network.html', controller: 'NetworkController' }); @@ -40,14 +38,14 @@ $stateProvider.state('articles', { url: '/articles?page', - templateUrl: tplBase + '/articles/index.html', + templateUrl: Vipra.const.tplBase + '/articles/index.html', controller: 'ArticlesIndexController', reloadOnSearch: false }); $stateProvider.state('articles.show', { url: '/:id', - templateUrl: tplBase + '/articles/show.html', + templateUrl: Vipra.const.tplBase + '/articles/show.html', controller: 'ArticlesShowController' }); @@ -55,29 +53,35 @@ $stateProvider.state('topics', { url: '/topics?page', - templateUrl: tplBase + '/topics/index.html', + templateUrl: Vipra.const.tplBase + '/topics/index.html', controller: 'TopicsIndexController', reloadOnSearch: false }); $stateProvider.state('topics.show', { url: '/:id', - templateUrl: tplBase + '/topics/show.html', + 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' + }) + // states: words $stateProvider.state('words', { url: '/words?page', - templateUrl: tplBase + '/words/index.html', + templateUrl: Vipra.const.tplBase + '/words/index.html', controller: 'WordsIndexController', reloadOnSearch: false }); $stateProvider.state('words.show', { url: '/:id', - templateUrl: tplBase + '/words/show.html', + templateUrl: Vipra.const.tplBase + '/words/show.html', controller: 'WordsShowController' }); diff --git a/vipra-ui/app/js/config.js b/vipra-ui/app/js/config.js new file mode 100644 index 0000000000000000000000000000000000000000..67ae35dcc03490f07c31be2baef975e706f60100 --- /dev/null +++ b/vipra-ui/app/js/config.js @@ -0,0 +1,10 @@ +(function() { + + window.Vipra = window.Vipra || {}; + + Vipra.config = { + restUrl: '//' + location.hostname + ':8000/vipra/rest', + websocketUrl: '//' + location.hostname + ':8000/vipra/ws' + }; + +})(); \ No newline at end of file diff --git a/vipra-ui/app/js/constants.js b/vipra-ui/app/js/constants.js new file mode 100644 index 0000000000000000000000000000000000000000..b60fe8af4e2d41e875fc7bf7a33b7bb6cda0fb65 --- /dev/null +++ b/vipra-ui/app/js/constants.js @@ -0,0 +1,12 @@ +(function() { + + window.Vipra = window.Vipra || {}; + + Vipra.const = { + tplBase: 'html', + latestItems: 3, + searchResults: 10, + pageSize: 100 + }; + +})(); \ No newline at end of file diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js index ae8935fd3dd446f873d682de949680e001de4e0d..49ac6552309c2f3389ce550caa7e4caf45918b86 100644 --- a/vipra-ui/app/js/controllers.js +++ b/vipra-ui/app/js/controllers.js @@ -9,11 +9,6 @@ 'vipra.factories' ]); - var latestItemsCount = 3, - searchItemsCount = 10, - pageSize = 100, - paginationPadding = 4; - app.controller('RootController', ['$scope', '$state', function($scope, $state) { $scope.$state = $state; }]); @@ -26,15 +21,15 @@ $scope.search = $location.search().query; - ArticleFactory.query({limit:latestItemsCount, sort:'-created'}, function(response) { + ArticleFactory.query({limit:Vipra.const.latestItems, sort:'-created'}, function(response) { $scope.latestArticles = response.data; }); - TopicFactory.query({limit:latestItemsCount, sort:'-created'}, function(response) { + TopicFactory.query({limit:Vipra.const.latestItems, sort:'-created'}, function(response) { $scope.latestTopics = response.data; }); - WordFactory.query({limit:latestItemsCount, sort:'-created'}, function(response) { + WordFactory.query({limit:Vipra.const.latestItems, sort:'-created'}, function(response) { $scope.latestWords = response.data; }); @@ -42,7 +37,7 @@ if($scope.search) { $location.search('query', $scope.search); $scope.searching = true; - SearchFactory.query({limit:searchItemsCount, query:$scope.search}, function(response) { + SearchFactory.query({limit:Vipra.const.searchResults, query:$scope.search}, function(response) { $scope.searching = false; $scope.searchResults = response.data; $scope.queryTime = response.$queryTime; @@ -255,7 +250,7 @@ function($scope, $state, $stateParams, ArticleFactory, Store) { $scope.page = Math.max($stateParams.page || 1, 1); - $scope.limit = pageSize; + $scope.limit = Vipra.const.pageSize; $scope.sort = Store('sortarticles') || 'date'; $scope.order = Store('orderarticles') || ''; @@ -291,10 +286,10 @@ ArticleFactory.get({id: $stateParams.id}, function(response) { $scope.article = response.data; - $scope.article.text = createInitial($scope.article.text); - $scope.articleDate = formatDate($scope.article.date); - $scope.articleCreated = formatDateTime($scope.article.created); - $scope.articleModified = formatDateTime($scope.article.modified); + $scope.article.text = Vipra.createInitial($scope.article.text); + $scope.articleDate = Vipra.formatDate($scope.article.date); + $scope.articleCreated = Vipra.formatDateTime($scope.article.created); + $scope.articleModified = Vipra.formatDateTime($scope.article.modified); $scope.articleMeta = response.meta; $scope.queryTime = response.$queryTime; @@ -302,7 +297,7 @@ var topicShareSeries = [], topics = $scope.article.topics; for(var i = 0; i < topics.length; i++) { - var share = toPercent(topics[i].count / $scope.article.stats.wordCount); + var share = Vipra.toPercent(topics[i].count / $scope.article.stats.wordCount); topics[i].share = share; topicShareSeries.push({name: topics[i].topic.name.ellipsize(20), y: share}); } @@ -339,7 +334,7 @@ function($scope, $stateParams, Store, TopicFactory) { $scope.page = Math.max($stateParams.page || 1, 1); - $scope.limit = pageSize; + $scope.limit = Vipra.const.pageSize; $scope.sort = Store('sorttopics') || 'name'; $scope.order = Store('ordertopics') || ''; @@ -375,8 +370,8 @@ TopicFactory.get({id: $stateParams.id}, function(response) { $scope.topic = response.data; - $scope.topicCreated = formatDateTime($scope.topic.created); - $scope.topicModified = formatDateTime($scope.topic.modified); + $scope.topicCreated = Vipra.formatDateTime($scope.topic.created); + $scope.topicModified = Vipra.formatDateTime($scope.topic.modified); $scope.topicMeta = response.meta; $scope.queryTime = response.$queryTime; }); @@ -397,7 +392,7 @@ $scope.isRename = false; }, function(response) { if(response.data) - $scope.renameErrors = getErrors(response.data.errors); + $scope.renameErrors = Vipra.getErrors(response.data.errors); }); } else { $scope.isRename = false; @@ -414,6 +409,19 @@ }]); + /** + * Topic Show Articles route + */ + app.controller('TopicArticlesController', ['$scope', '$stateParams', 'TopicFactory', + function($scope, $stateParams, TopicFactory) { + + TopicFactory.articles({id: $stateParams.id}, function(response) { + $scope.articles = response.data; + $scope.queryTime = response.$queryTime; + }); + + }]); + /**************************************************************************** * Word Controllers ****************************************************************************/ @@ -459,7 +467,7 @@ WordFactory.get({id: $stateParams.id}, function(response) { $scope.word = response.data; - $scope.wordCreated = formatDateTime($scope.word.created); + $scope.wordCreated = Vipra.formatDateTime($scope.word.created); $scope.wordMeta = response.meta; $scope.queryTime = response.$queryTime; }); @@ -486,8 +494,9 @@ $scope.calculatePages = function() { var pages = [], max = Math.ceil($scope.total/$scope.limit*1.0), - start = Math.max($scope.page - paginationPadding, 1), - end = Math.min(Math.max($scope.page + paginationPadding, start + paginationPadding * 2), max); + pad = 4, + start = Math.max($scope.page - pad, 1), + end = Math.min(Math.max($scope.page + pad, start + pad * 2), max); for(var i = start; i <= end; i++) { pages.push(i); } diff --git a/vipra-ui/app/js/directives.js b/vipra-ui/app/js/directives.js index 125427e7a876af5bd63c0807ad93d85d55f130ad..48364ce22994f8a1cb73e01273289a21a1fe96ef 100644 --- a/vipra-ui/app/js/directives.js +++ b/vipra-ui/app/js/directives.js @@ -8,8 +8,6 @@ 'ui.router' ]); - var slideDuration = 250; - app.directive('topicLink', function() { return { scope: { @@ -150,7 +148,7 @@ transclude: true, replace: true, link: function($scope, $elem, $attrs) { - $scope.dropdownId = randomId(); + $scope.dropdownId = Vipra.randomId(); $scope.label = $attrs.label; $scope.align = 'dropdown-menu-left'; if($attrs.align === 'right') diff --git a/vipra-ui/app/js/factories.js b/vipra-ui/app/js/factories.js index 37a1632fc03d42fbb7975c636cc8095ae4903da5..9224eb92350d82deec477ead6cc79143a5f82106 100644 --- a/vipra-ui/app/js/factories.js +++ b/vipra-ui/app/js/factories.js @@ -6,37 +6,35 @@ var app = angular.module('vipra.factories', []); - var endpoint = '//' + location.hostname + ':8080/vipra/rest'; - app.factory('ArticleFactory', ['$resource', function($resource) { - return $resource(endpoint + '/articles/:id', {}, { + return $resource(Vipra.config.restUrl + '/articles/:id', {}, { query: { isArray: false } }); }]); app.factory('TopicFactory', ['$resource', function($resource) { - return $resource(endpoint + '/topics/:id', {}, { + return $resource(Vipra.config.restUrl + '/topics/:id', {}, { query: { isArray: false }, update: { method: 'PUT' }, - articles: { url: endpoint + '/topics/:id/articles' } + articles: { url: Vipra.config.restUrl + '/topics/:id/articles' } }); }]); app.factory('WordFactory', ['$resource', function($resource) { - return $resource(endpoint + '/words/:id', {}, { + return $resource(Vipra.config.restUrl + '/words/:id', {}, { query: { isArray: false }, - topics: { url: endpoint + '/words/:id/topics' } + topics: { url: Vipra.config.restUrl + '/words/:id/topics' } }); }]); app.factory('SearchFactory', ['$resource', function($resource) { - return $resource(endpoint + '/search', {}, { + return $resource(Vipra.config.restUrl + '/search', {}, { query: { isArray: false } }); }]); + // https://gist.github.com/Fluidbyte/4718380 app.factory('Store', ['$state', function($state) { - // https://gist.github.com/Fluidbyte/4718380 return function(key, value) { key += '-' + $state.current.name; var lsSupport = false; diff --git a/vipra-ui/app/js/filters.js b/vipra-ui/app/js/filters.js index ae98ac22d553e57fa2b1f32afc2cfe40ddbd84ed..b4a1b8aea5399fcf1dd773fe15e7c4fedd3b01f0 100644 --- a/vipra-ui/app/js/filters.js +++ b/vipra-ui/app/js/filters.js @@ -7,15 +7,15 @@ var app = angular.module('vipra.filters', []); app.filter('toPercent', function() { - return toPercent; + return Vipra.toPercent; }); app.filter('formatDate', function() { - return formatDate; + return Vipra.formatDate; }); app.filter('formatDateTime', function() { - return formatDateTime; + return Vipra.formatDateTime; }); })(); \ No newline at end of file diff --git a/vipra-ui/app/js/helpers.js b/vipra-ui/app/js/helpers.js index 64f87779659b9a80b8caaf30409e084f12f1947c..4bcc959e90064fee5025f1d835d51282ee66c591 100644 --- a/vipra-ui/app/js/helpers.js +++ b/vipra-ui/app/js/helpers.js @@ -4,34 +4,36 @@ ******************************************************************************/ (function() { - window.formatDate = function(date) { + window.Vipra = window.Vipra || {}; + + /** + * Helpers + */ + + Vipra.formatDate = function(date) { return new Date(date).toLocaleDateString(); }; - window.formatDateTime = function(date) { + Vipra.formatDateTime = function(date) { date = new Date(date); return date.toLocaleDateString() + " " + date.toLocaleTimeString(); }; - window.toPercent = function(input) { + Vipra.toPercent = function(input) { if(typeof input !== 'number') input = parseInt(input, 10); return Math.round(input * 100); }; - window.createInitial = function(text) { + Vipra.createInitial = function(text) { return '<span class="initial">' + text.substring(0, 1) + "</span>" + text.substring(1); }; - window.randomId = function() { + Vipra.randomId = function() { return 'id' + Math.random().toString(36).substring(7); }; - window.console = window.console || { - log: function () {} - }; - - window.getErrors = function(errors) { + Vipra.getErrors = function(errors) { var html = []; if(errors && errors.length) { for(var i = 0; i < errors.length; i++) @@ -40,21 +42,32 @@ return html.join('<br>'); } - String.prototype.ellipsize = function(max) { - max = max || 20; - if(this.length > max) { - return this.substring(0, max) + '...'; - } - return this; - }; + /** + * Polyfills + */ - String.prototype.multiline = function(max) { - return this.split(new RegExp("((?:\\w+ ){" + max + "})", "g")).filter(Boolean).join("\n"); - }; + if(typeof String.prototype.ellipsize === 'undefined') + String.prototype.ellipsize = function(max) { + max = max || 20; + if(this.length > max) { + return this.substring(0, max) + '...'; + } + return this; + }; + + if(typeof String.prototype.multiline === 'undefined') + String.prototype.multiline = function(max) { + return this.split(new RegExp("((?:\\w+ ){" + max + "})", "g")).filter(Boolean).join("\n"); + }; if(typeof String.prototype.startsWith === 'undefined') String.prototype.startsWith = function(start) { return this.lastIndexOf(start, 0) === 0; }; + if(typeof window.console === 'undefined') + window.console = { + log: function () {} + }; + })(); \ No newline at end of file