Merge pull request #6 from alexfriesen/gh-pages

md cleanup, responsive playerlist, chat improvements
This commit is contained in:
Garry Newman
2016-06-08 22:42:01 +01:00
11 changed files with 204 additions and 215 deletions
+10 -1
View File
@@ -1,6 +1,15 @@
<div ng-controller="ChatController" id="ChatController"> <div ng-controller="ChatController" id="ChatController">
<div id="vertical-container" style="height: 600px" class="Output scrollable"><div ng-repeat="line in Output" ng-class="line.Class"><span class="chatname"><a href="#/{{address}}/player/{{line.UserId}}/" style="color: {{line.Color}}">{{line.Username}}</a>:</span> {{line.Message}}</div></div> <div style="height: 600px" class="Output scrollable">
<div ng-repeat="line in Output" ng-class="line.Class">
<span class="badge">
{{(line.Time * 1000) | date:'shortTime'}}
<a href="#/{{address}}/player/{{line.UserId}}/" style="color: {{line.Color}}">
{{line.Username}}
</a>
</span> {{line.Message}}
</div>
</div>
<form ng-submit="SubmitCommand()" class="md-inline-form"> <form ng-submit="SubmitCommand()" class="md-inline-form">
<div> <div>
+12 -9
View File
@@ -4,24 +4,25 @@
<h2>Connect</h2> <h2>Connect</h2>
<div class="alert alert-danger" ng-show="LastErrorMessage != null">{{LastErrorMessage}}</div> <div class="alert alert-danger" ng-show="LastErrorMessage != null">
{{LastErrorMessage}}
</div>
<p>Enter the address (including rcon port) and the rcon password below to connect.</p> <p>Enter the address (including rcon port) and the rcon password below to connect.</p>
<form ng-submit="Connect()"> <form ng-submit="Connect()">
<div class="md-inline-form">
<div class="md-block form-group" flex-gt-xs> <div class="form-group">
<label for="conncetion-adress">Address</label> <label for="conncetion-adress">Address</label>
<input required minlength=5 ng-model="Address" name="Address" class="form-control" placeholder="xxx.xxx.xxx.xxx:28016" id="conncetion-adress"> <input required minlength=5 ng-model="Address" name="Address" class="form-control" placeholder="xxx.xxx.xxx.xxx:28016" id="conncetion-adress">
</div> </div>
<div class="md-block form-group" flex-gt-xs> <div class="form-group">
<label for="conncetion-password">Password</label> <label for="conncetion-password">Password</label>
<input required minlength=2 type="password" ng-model="Password" name="Password" class="form-control" id="conncetion-password"> <input required minlength=2 type="password" ng-model="Password" name="Password" class="form-control" id="conncetion-password">
</div> </div>
<div class="checkbox right"> <div class="checkbox pull-right">
<label> <label>
<input type="checkbox" ng-model="SaveConnection"> Save Connection <input type="checkbox" ng-model="SaveConnection"> Save Connection
</label> </label>
@@ -29,8 +30,6 @@
<button type="submit" class="btn btn-primary btn-block" ng-click="Connect()">Connect</button> <button type="submit" class="btn btn-primary btn-block" ng-click="Connect()">Connect</button>
</div>
</form> </form>
</div> </div>
@@ -39,10 +38,14 @@
<div ng-show="PreviousConnects.length > 0"> <div ng-show="PreviousConnects.length > 0">
<h2>Saved Connections</h2> <h2>Saved Connections</h2>
<ul ng-repeat="c in PreviousConnects | orderBy:array:true | limitTo:connectionsLimit"> <ul ng-repeat="c in PreviousConnects | orderBy:array:true | limitTo:connectionsLimit">
<li><a ng-click="ConnectTo( c )" href="#/{{c.Address}}/info">{{c.Address}}</a> - {{c.date | date:'short'}}</li> <li>
<a ng-click="ConnectTo( c )" href="#/{{c.Address}}/info">{{c.Address}}</a> - {{c.date | date:'short'}}
</li>
</ul> </ul>
<div ng-show="PreviousConnects.length > connectionsLimit || connectionsLimit === undefined"> <div ng-show="PreviousConnects.length > connectionsLimit || connectionsLimit === undefined">
<button type="submit" class="btn btn-primary btn-block" ng-click="toggleConnectionsLimit()">Show {{(connectionsLimit === undefined) ? 'less' : 'more'}}</button> <button type="submit" class="btn btn-primary btn-block" ng-click="toggleConnectionsLimit()">
Show {{(connectionsLimit === undefined) ? 'less' : 'more'}}
</button>
</div> </div>
</div> </div>
+2 -2
View File
@@ -1,10 +1,10 @@
<div ng-controller="ConsoleController" id="ConsoleController"> <div ng-controller="ConsoleController" id="ConsoleController">
<div id="vertical-container" style="height: 600px" class="Output scrollable"> <div style="height: 600px" class="Output scrollable">
<div ng-repeat="line in Output" ng-class="line.Class">{{line.Message}}</div> <div ng-repeat="line in Output" ng-class="line.Class">{{line.Message}}</div>
</div> </div>
<form ng-submit="SubmitCommand()" class="md-inline-form"> <form ng-submit="SubmitCommand()">
<input type="text" class="form-control" ng-model="Command" ng-keyup="KeyUp($event)" /> <input type="text" class="form-control" ng-model="Command" ng-keyup="KeyUp($event)" />
</form> </form>
+1 -1
View File
@@ -1,5 +1,5 @@
<div ng-controller="PlayerInfoController" flex layout="column" id="PlayerInfoController"> <div ng-controller="PlayerInfoController" id="PlayerInfoController">
<div class="row"> <div class="row">
+34 -55
View File
@@ -1,63 +1,42 @@
<div ng-controller="PlayerXpController" flex layout="column" id="PlayerXpController"> <div ng-controller="PlayerXpController" id="PlayerXpController">
<md-content flex layout-padding>
<h2>Statistics for player <a href="#/{{address}}/player/{{userid}}/">{{userid}}</a></h2>
<div layout="row" layout-sm="column">
<div flex>
<md-card>
<md-card-title>
<md-card-title-text>
<span class="md-headline">Xp Income Sources</span>
<span class="md-subhead">Where has this user got their Xp from</span>
</md-card-title-text>
</md-card-title>
<md-card-content layout="row" layout-align="space-between">
<md-card-title-media flex>
<nvd3 flex options="Sources.Options" data="Sources.Data"></nvd3>
</md-card-title-media>
</md-card-content>
</md-card>
</div>
<div flex>
<md-card>
<md-card-title>
<md-card-title-text>
<span class="md-headline">Xp Outgoing Sources</span>
<span class="md-subhead">Where has this user spent their Xp</span>
</md-card-title-text>
</md-card-title>
<md-card-content layout="row" layout-align="space-between">
<md-card-title-media flex>
<nvd3 flex options="Outgoings.Options" data="Outgoings.Data"></nvd3>
</md-card-title-media>
</md-card-content>
</md-card>
</div>
<div class="row">
<div class="col-md-12">
<h2>
Statistics for player: <a href="#/{{address}}/player/{{userid}}/">{{userid}}</a>
</h2>
</div> </div>
</div>
<div flex> <div class="row">
<md-card> <div class="col-md-12">
<md-card-title> <h3>Xp Income Sources</h3>
<md-card-title-text> <h5>Where has this user got their Xp from</h5>
<span class="md-headline">Xp Over Time</span> <div class="panel">
<span class="md-subhead">How much XP has this user got over time</span> <nvd3 options="Sources.Options" data="Sources.Data"></nvd3>
</md-card-title-text> </div>
</md-card-title>
<md-card-content layout="row" layout-align="space-between">
<md-card-title-media flex>
<nvd3 flex options="OverTime.Options" data="OverTime.Data"></nvd3>
</md-card-title-media>
</md-card-content>
</md-card>
</div> </div>
</div>
</md-content> <div class="row">
<div class="col-md-12">
<h3>Xp Outgoing Sources</h3>
<h5>Where has this user spent their Xp</h5>
<div class="panel">
<nvd3 flex options="Outgoings.Options" data="Outgoings.Data"></nvd3>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h3>Xp Over Time</h3>
<h5>How much XP has this user got over time</h5>
<div class="panel">
<nvd3 flex options="OverTime.Options" data="OverTime.Data"></nvd3>
</div>
</div>
</div>
</div> </div>
+80 -75
View File
@@ -1,95 +1,100 @@
<div ng-controller="PlayerListController" flex layout="column" id="PlayerListController"> <div ng-controller="PlayerListController" id="PlayerListController">
<div class="right row"> <div class="row">
<div class="float-left"> <div class="col-xs-6">
<input ng-model="FilterText" style="width: 300px" class="form-control" placeholder="Filter.."> <input ng-model="FilterText" class="form-control" placeholder="Filter..">
</div> </div>
<div class="btn-group"> <div class="col-xs-6">
<button ng-click="Refresh()" class="btn btn-default" type="button"><i class="glyphicon glyphicon-refresh"></i></button> <button ng-click="Refresh()" class="btn btn-default pull-right" type="button"><span class="glyphicon glyphicon-refresh"></span></button>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<table> <div class="col-sm-12">
<thead> <div class="table-responsive">
<tr> <table class="table">
<th>Actions</th> <thead>
<tr>
<th>Actions</th>
<th ng-click="Order( 'DisplayName' )" ng-class="SortClass( 'DisplayName' )">Username</th> <th ng-click="Order( 'DisplayName' )" ng-class="SortClass( 'DisplayName' )">Username</th>
<th style="text-align: right" ng-click="Order( 'SteamID' )" ng-class="SortClass( 'SteamID' )">Steamid</th> <th class="text-right" ng-click="Order( 'SteamID' )" ng-class="SortClass( 'SteamID' )">Steamid</th>
<th style="text-align: right" ng-click="Order( 'VoiationLevel' )" ng-class="SortClass( 'VoiationLevel' )">Voilation</th> <th class="text-right" ng-click="Order( 'VoiationLevel' )" ng-class="SortClass( 'VoiationLevel' )">Voilation</th>
<th style="text-align: right" ng-click="Order( 'CurrentLevel' )" ng-class="SortClass( 'CurrentLevel' )">Level</th> <th class="text-right" ng-click="Order( 'CurrentLevel' )" ng-class="SortClass( 'CurrentLevel' )">Level</th>
<th style="text-align: right" ng-click="Order( 'UnspentXp' )" ng-class="SortClass( 'UnspentXp' )">XP</th> <th class="text-right" ng-click="Order( 'UnspentXp' )" ng-class="SortClass( 'UnspentXp' )">XP</th>
<th style="text-align: right" ng-click="Order( 'Address' )" ng-class="SortClass( 'Address' )">Address</th> <th class="text-right" ng-click="Order( 'Address' )" ng-class="SortClass( 'Address' )">Address</th>
<th ng-click="Order( 'Ping' )" ng-class="SortClass( 'Ping' )">Ping</th> <th class="text-right" ng-click="Order( 'Ping' )" ng-class="SortClass( 'Ping' )">Ping</th>
<th ng-click="Order( 'ConnectedSeconds' )" ng-class="SortClass( 'ConnectedSeconds' )">Duration</th> <th class="text-right" ng-click="Order( 'ConnectedSeconds' )" ng-class="SortClass( 'ConnectedSeconds' )">Duration</th>
</tr> </tr>
</thead> </thead>
<tr ng-repeat="line in Players | orderBy: OrderBy | filter:FilterText" href="#/{{address}}/player/{{line.SteamID}}/"> <tr ng-repeat="line in Players | orderBy: OrderBy | filter:FilterText" href="#/{{address}}/player/{{line.SteamID}}/">
<td> <td>
<div class="btn-group"> <div class="btn-group">
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-cog"></span> <span class="caret"></span> <span class="glyphicon glyphicon-cog"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li> <li>
<a href="#/{{address}}/player/{{line.SteamID}}/xp"> <a href="#/{{address}}/player/{{line.SteamID}}/xp">
<span class="glyphicon glyphicon-education"></span> <span class="glyphicon glyphicon-education"></span>
XP Info XP Info
</a> </a>
</li> </li>
<li> <li>
<a href="http://www.steamcommunity.com/profiles/{{line.SteamID}}" target="_blank"> <a href="http://www.steamcommunity.com/profiles/{{line.SteamID}}" target="_blank">
<span class="glyphicon glyphicon-info-sign"></span> <span class="glyphicon glyphicon-info-sign"></span>
Steam Profile Steam Profile
</a> </a>
</li> </li>
<li class="divider"></li> <li class="divider"></li>
<li> <li>
<a href ng-click="KickPlayer(line.SteamID)"> <a href ng-click="KickPlayer(line.SteamID)">
<span class="glyphicon glyphicon-trash"></span> <span class="glyphicon glyphicon-trash"></span>
Kick Kick
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
</td> </td>
<td> <td>
<a href="#/{{address}}/player/{{line.SteamID}}/">{{line.DisplayName}}</a> <a href="#/{{address}}/player/{{line.SteamID}}/">{{line.DisplayName}}</a>
</td> </td>
<td style="text-align: right"> <td style="text-align: right">
<a href="#/{{address}}/player/{{line.SteamID}}/">{{line.SteamID}}</a> <a href="#/{{address}}/player/{{line.SteamID}}/">{{line.SteamID}}</a>
</td> </td>
<td style="text-align: right" ng-class="{fade: line.VoiationLevel == 0 }"> <td class="text-right" ng-class="{fade: line.VoiationLevel == 0 }">
{{line.VoiationLevel}} {{line.VoiationLevel}}
</td> </td>
<td style="text-align: right"> <td class="text-right">
{{line.CurrentLevel}} {{line.CurrentLevel}}
</td> </td>
<td style="text-align: right"> <td class="text-right">
<a href="#/{{address}}/player/{{line.SteamID}}/xp">{{line.UnspentXp}}</a> <a href="#/{{address}}/player/{{line.SteamID}}/xp">{{line.UnspentXp}}</a>
</td> </td>
<td style="text-align: right"> <td class="text-right">
{{line.Address}} {{line.Address}}
</td> </td>
<td class="md-list-item-text" style="text-align: right">{{line.Ping}}</td> <td class="text-right">
{{line.Ping}}
</td>
<td style="text-align: right"> <td class="text-right">
{{line.ConnectedSeconds | SecondsToDuration}} {{line.ConnectedSeconds | SecondsToDuration}}
</td> </td>
<md-divider></md-divider>
</tr>
</table>
<md-divider></md-divider>
</tr>
</table>
</div>
</div>
</div> </div>
</div> </div>
+9 -2
View File
@@ -3,15 +3,20 @@
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-sm-12">
<h2 class="center"> <h2 class="center">
{{info.Hostname}} {{info.Hostname}}
<div class="btn-group pull-right"> <div class="btn-group pull-right">
<button ng-click="Refresh()" class="btn btn-default" type="button"><i class="glyphicon glyphicon-refresh"></i></button> <button ng-click="refresh()" class="btn btn-default" type="button"><i class="glyphicon glyphicon-refresh"></i></button>
</div> </div>
</h2> </h2>
</div> </div>
<div class="col-sm-4" ng-repeat="(key, value) in info">
<p>{{key}}: <span class="badge">{{value}}</span></p>
</div>
<!--
<div class="col-sm-4"> <div class="col-sm-4">
<p>Map: {{info.Map}}</p> <p>Map: {{info.Map}}</p>
</div> </div>
@@ -48,6 +53,8 @@
<p>Queued: <span class="badge">{{info.Queued}}</span></p> <p>Queued: <span class="badge">{{info.Queued}}</span></p>
</div> </div>
-->
</div> </div>
</div> </div>
+38 -9
View File
@@ -3,7 +3,7 @@ app.controller( 'ChatController', ChatController );
function ChatController( $scope, rconService, $timeout ) function ChatController( $scope, rconService, $timeout )
{ {
$scope.Output = new Array(); $scope.Output = [];
$scope.SubmitCommand = function () $scope.SubmitCommand = function ()
{ {
@@ -13,21 +13,48 @@ function ChatController( $scope, rconService, $timeout )
$scope.$on( "OnMessage", function ( event, msg ) $scope.$on( "OnMessage", function ( event, msg )
{ {
if ( msg.Type != "Chat" ) return; if ( msg.Type !== "Chat" ) return;
$scope.OnMessage( JSON.parse( msg.Message ) ); $scope.OnMessage( JSON.parse( msg.Message ) );
} ); });
$scope.OnMessage = function( msg ) $scope.OnMessage = function( msg )
{ {
$scope.Output.push( msg ); $scope.Output.push( msg );
$timeout( $scope.ScrollToBottom, 50 );
if($scope.isOnBottom()) {
$scope.ScrollToBottom();
}
} }
$scope.ScrollToBottom = function() $scope.ScrollToBottom = function()
{ {
// TODO - don't scroll if we're not at the bottom ! var element = $( "#ChatController .Output" );
$( "#ChatController .Output" ).scrollTop( $( "#ChatController .Output" )[0].scrollHeight );
$timeout( function() {
element.scrollTop( element.prop('scrollHeight') );
}, 50 );
}
$scope.isOnBottom = function()
{
// get jquery element
var element = $( "#ChatController .Output" );
// height of the element
var height = element.height();
// scroll position from top position
var scrollTop = element.scrollTop();
// full height of the element
var scrollHeight = element.prop('scrollHeight');
if((scrollTop + height) > (scrollHeight - 10)) {
return true;
}
return false;
} }
// //
@@ -36,13 +63,15 @@ function ChatController( $scope, rconService, $timeout )
// //
$scope.GetHistory = function () $scope.GetHistory = function ()
{ {
console.log( "GetHistory" );
rconService.Request( "chat.tail 512", $scope, function ( msg ) rconService.Request( "chat.tail 512", $scope, function ( msg )
{ {
var messages = JSON.parse( msg.Message ); var messages = JSON.parse( msg.Message );
messages.forEach( function ( x ) { $scope.OnMessage( x ); } ); messages.forEach( function ( message ) {
$scope.OnMessage( message );
});
$scope.ScrollToBottom();
} ); } );
} }
+12 -12
View File
@@ -14,20 +14,20 @@ function PlayerListController( $scope, rconService, $interval )
}); });
} }
$scope.Order = function( o ) $scope.Order = function( field )
{ {
if ( $scope.OrderBy == o ) if ( $scope.OrderBy === field )
{ {
o = '-' + o; field = '-' + field;
} }
$scope.OrderBy = o; $scope.OrderBy = field;
} }
$scope.SortClass = function( o ) $scope.SortClass = function( field )
{ {
if ( $scope.OrderBy == o ) return "active"; if ( $scope.OrderBy === field ) return "sorting";
if ( $scope.OrderBy == "-" + o ) return "active descending"; if ( $scope.OrderBy === "-" + field ) return "sorting descending";
return null; return null;
} }
@@ -41,10 +41,10 @@ function PlayerListController( $scope, rconService, $interval )
rconService.InstallService( $scope, $scope.Refresh ) rconService.InstallService( $scope, $scope.Refresh )
var timer = $interval( function () // var timer = $interval( function ()
{ // {
//$scope.Refresh(); // //$scope.Refresh();
}.bind( this ), 1000 ); // }.bind( this ), 1000 );
$scope.$on( '$destroy', function () { $interval.cancel( timer ) } ) //$scope.$on( '$destroy', function () { $interval.cancel( timer ) } )
} }
+3 -7
View File
@@ -5,16 +5,12 @@ function ServerInfoController( $scope, rconService, $routeParams )
{ {
$scope.info = {}; $scope.info = {};
$scope.Refresh = function () $scope.refresh = function ()
{ {
rconService.Request( 'serverinfo', $scope, function ( msg ) rconService.Request( 'serverinfo', $scope, function ( msg ) {
{
$scope.info = JSON.parse( msg.Message ); $scope.info = JSON.parse( msg.Message );
// $scope.Players = JSON.parse( msg.Message );
console.log($scope.info);
}); });
} }
rconService.InstallService( $scope, $scope.Refresh ); rconService.InstallService( $scope, $scope.refresh );
} }
+3 -42
View File
@@ -21,19 +21,11 @@ a:hover
text-decoration: none; text-decoration: none;
} }
.right
{
text-align: right;
}
.center .center
{ {
text-align: center; text-align: center;
} }
.float-left { float: left; }
#ConsoleController .Output #ConsoleController .Output
{ {
font-family: monospace; font-family: monospace;
@@ -68,53 +60,22 @@ a:hover
background-color: #428bca; background-color: #428bca;
} }
table
{
border-collapse: collapse;
border-spacing: 0;
width: 100%;
}
.fade .fade
{ {
opacity: 0.5; opacity: 0.5;
} }
th, td .table > tbody > tr:hover
{ {
padding: 5px 8px; background-color: rgba( 255, 255, 255, 0.3 );
border-bottom: 1px solid #414758;
} }
th.active .table > thead > tr > th.sorting
{ {
color: #1bc98e; color: #1bc98e;
border-color: #1bc98e; border-color: #1bc98e;
} }
th
{
border-bottom: 2px solid #414758;
border-radius: 10px;
}
th:first-child
{
border-radius: 6px;
}
th:last-child
{
border-radius: 0 6px 0 0;
}
th:only-child
{
border-radius: 6px 6px 0 0;
}
.btn-default, .btn-default:active, .form-control .btn-default, .btn-default:active, .form-control
{ {
background: rgba( 255, 255, 255, 0.3 ); background: rgba( 255, 255, 255, 0.3 );