Added loading spinner

pull/733/head
Jamie.Rees 8 years ago
parent f73d12e095
commit 63c2744336

@ -0,0 +1,25 @@
(function(){
angular.module('ngLoadingSpinner', ['angularSpinner'])
.directive('usSpinner', ['$http', '$rootScope' ,function ($http, $rootScope){
return {
link: function (scope, elm, attrs)
{
$rootScope.spinnerActive = false;
scope.isLoading = function () {
return $http.pendingRequests.length > 0;
};
scope.$watch(scope.isLoading, function (loading)
{
$rootScope.spinnerActive = loading;
if(loading){
elm.removeClass('ng-hide');
}else{
elm.addClass('ng-hide');
}
});
}
};
}]);
}).call(this);

@ -0,0 +1,2 @@
!function(a){"use strict";function b(a,b){a.module("angularSpinner",[]).factory("usSpinnerService",["$rootScope",function(a){var b={};return b.spin=function(b){a.$broadcast("us-spinner:spin",b)},b.stop=function(b){a.$broadcast("us-spinner:stop",b)},b}]).directive("usSpinner",["$window",function(c){return{scope:!0,link:function(d,e,f){function g(){d.spinner&&d.spinner.stop()}var h=b||c.Spinner;d.spinner=null,d.key=a.isDefined(f.spinnerKey)?f.spinnerKey:!1,d.startActive=a.isDefined(f.spinnerStartActive)?f.spinnerStartActive:d.key?!1:!0,d.spin=function(){d.spinner&&d.spinner.spin(e[0])},d.stop=function(){d.startActive=!1,g()},d.$watch(f.usSpinner,function(a){g(),d.spinner=new h(a),(!d.key||d.startActive)&&d.spinner.spin(e[0])},!0),d.$on("us-spinner:spin",function(a,b){b===d.key&&d.spin()}),d.$on("us-spinner:stop",function(a,b){b===d.key&&d.stop()}),d.$on("$destroy",function(){d.stop(),d.spinner=null})}}}])}"function"==typeof define&&define.amd?define(["angular","spin"],b):b(a.angular)}(window);
//# sourceMappingURL=angular-spinner.min.js.map

@ -1,4 +1,4 @@
(function() {
module = angular.module('PlexRequests', []);
module = angular.module('PlexRequests', ['ngLoadingSpinner']);
module.constant("moment", moment);
}());

@ -0,0 +1,2 @@
// http://spin.js.org/#v2.3.2
!function(a,b){"object"==typeof module&&module.exports?module.exports=b():"function"==typeof define&&define.amd?define(b):a.Spinner=b()}(this,function(){"use strict";function a(a,b){var c,d=document.createElement(a||"div");for(c in b)d[c]=b[c];return d}function b(a){for(var b=1,c=arguments.length;c>b;b++)a.appendChild(arguments[b]);return a}function c(a,b,c,d){var e=["opacity",b,~~(100*a),c,d].join("-"),f=.01+c/d*100,g=Math.max(1-(1-a)/b*(100-f),a),h=j.substring(0,j.indexOf("Animation")).toLowerCase(),i=h&&"-"+h+"-"||"";return m[e]||(k.insertRule("@"+i+"keyframes "+e+"{0%{opacity:"+g+"}"+f+"%{opacity:"+a+"}"+(f+.01)+"%{opacity:1}"+(f+b)%100+"%{opacity:"+a+"}100%{opacity:"+g+"}}",k.cssRules.length),m[e]=1),e}function d(a,b){var c,d,e=a.style;if(b=b.charAt(0).toUpperCase()+b.slice(1),void 0!==e[b])return b;for(d=0;d<l.length;d++)if(c=l[d]+b,void 0!==e[c])return c}function e(a,b){for(var c in b)a.style[d(a,c)||c]=b[c];return a}function f(a){for(var b=1;b<arguments.length;b++){var c=arguments[b];for(var d in c)void 0===a[d]&&(a[d]=c[d])}return a}function g(a,b){return"string"==typeof a?a:a[b%a.length]}function h(a){this.opts=f(a||{},h.defaults,n)}function i(){function c(b,c){return a("<"+b+' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">',c)}k.addRule(".spin-vml","behavior:url(#default#VML)"),h.prototype.lines=function(a,d){function f(){return e(c("group",{coordsize:k+" "+k,coordorigin:-j+" "+-j}),{width:k,height:k})}function h(a,h,i){b(m,b(e(f(),{rotation:360/d.lines*a+"deg",left:~~h}),b(e(c("roundrect",{arcsize:d.corners}),{width:j,height:d.scale*d.width,left:d.scale*d.radius,top:-d.scale*d.width>>1,filter:i}),c("fill",{color:g(d.color,a),opacity:d.opacity}),c("stroke",{opacity:0}))))}var i,j=d.scale*(d.length+d.width),k=2*d.scale*j,l=-(d.width+d.length)*d.scale*2+"px",m=e(f(),{position:"absolute",top:l,left:l});if(d.shadow)for(i=1;i<=d.lines;i++)h(i,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(i=1;i<=d.lines;i++)h(i);return b(a,m)},h.prototype.opacity=function(a,b,c,d){var e=a.firstChild;d=d.shadow&&d.lines||0,e&&b+d<e.childNodes.length&&(e=e.childNodes[b+d],e=e&&e.firstChild,e=e&&e.firstChild,e&&(e.opacity=c))}}var j,k,l=["webkit","Moz","ms","O"],m={},n={lines:12,length:7,width:5,radius:10,scale:1,corners:1,color:"#000",opacity:.25,rotate:0,direction:1,speed:1,trail:100,fps:20,zIndex:2e9,className:"spinner",top:"50%",left:"50%",shadow:!1,hwaccel:!1,position:"absolute"};if(h.defaults={},f(h.prototype,{spin:function(b){this.stop();var c=this,d=c.opts,f=c.el=a(null,{className:d.className});if(e(f,{position:d.position,width:0,zIndex:d.zIndex,left:d.left,top:d.top}),b&&b.insertBefore(f,b.firstChild||null),f.setAttribute("role","progressbar"),c.lines(f,c.opts),!j){var g,h=0,i=(d.lines-1)*(1-d.direction)/2,k=d.fps,l=k/d.speed,m=(1-d.opacity)/(l*d.trail/100),n=l/d.lines;!function o(){h++;for(var a=0;a<d.lines;a++)g=Math.max(1-(h+(d.lines-a)*n)%l*m,d.opacity),c.opacity(f,a*d.direction+i,g,d);c.timeout=c.el&&setTimeout(o,~~(1e3/k))}()}return c},stop:function(){var a=this.el;return a&&(clearTimeout(this.timeout),a.parentNode&&a.parentNode.removeChild(a),this.el=void 0),this},lines:function(d,f){function h(b,c){return e(a(),{position:"absolute",width:f.scale*(f.length+f.width)+"px",height:f.scale*f.width+"px",background:b,boxShadow:c,transformOrigin:"left",transform:"rotate("+~~(360/f.lines*k+f.rotate)+"deg) translate("+f.scale*f.radius+"px,0)",borderRadius:(f.corners*f.scale*f.width>>1)+"px"})}for(var i,k=0,l=(f.lines-1)*(1-f.direction)/2;k<f.lines;k++)i=e(a(),{position:"absolute",top:1+~(f.scale*f.width/2)+"px",transform:f.hwaccel?"translate3d(0,0,0)":"",opacity:f.opacity,animation:j&&c(f.opacity,f.trail,l+k*f.direction,f.lines)+" "+1/f.speed+"s linear infinite"}),f.shadow&&b(i,e(h("#000","0 0 4px #000"),{top:"2px"})),b(d,b(i,h(g(f.color,k),"0 0 1px rgba(0,0,0,.1)")));return d},opacity:function(a,b,c){b<a.childNodes.length&&(a.childNodes[b].style.opacity=c)}}),"undefined"!=typeof document){k=function(){var c=a("style",{type:"text/css"});return b(document.getElementsByTagName("head")[0],c),c.sheet||c.styleSheet}();var o=e(a("group"),{behavior:"url(#default#VML)"});!d(o,"transform")&&o.adj?i():j=d(o,"animation")}return h});

@ -80,7 +80,7 @@ namespace PlexRequests.UI.Helpers
{
$"<link rel=\"stylesheet\" href=\"{startUrl}/bootstrap.css\" type=\"text/css\"/>",
$"<link rel=\"stylesheet\" href=\"{startUrl}/font-awesome.css\" type=\"text/css\"/>",
$"<link rel=\"stylesheet\" href=\"{startUrl}/pace.min.css\" type=\"text/css\"/>",
//$"<link rel=\"stylesheet\" href=\"{startUrl}/pace.min.css\" type=\"text/css\"/>",
$"<link rel=\"stylesheet\" href=\"{startUrl}/awesome-bootstrap-checkbox.css\" type=\"text/css\"/>",
$"<link rel=\"stylesheet\" href=\"{startUrl}/base.css?v={Assembly}\" type=\"text/css\"/>",
$"<link rel=\"stylesheet\" href=\"{startUrl}/Themes/{settings.ThemeName}?v={Assembly}\" type=\"text/css\"/>",
@ -224,7 +224,9 @@ namespace PlexRequests.UI.Helpers
sb.Append($"<script src=\"{content}/Content/app/userManagement/userManagementController.js?v={Assembly}\" type=\"text/javascript\"></script>");
sb.Append($"<script src=\"{content}/Content/app/userManagement/userManagementService.js?v={Assembly}\" type=\"text/javascript\"></script>");
sb.Append($"<script src=\"{content}/Content/moment.min.js\"></script>");
sb.Append($"<script src=\"{content}/Content/spin.min.js\"></script>");
sb.Append($"<script src=\"{content}/Content/Angular/angular-spinner.min.js\"></script>");
sb.Append($"<script src=\"{content}/Content/Angular/angular-loading-spinner.js\"></script>");
return helper.Raw(sb.ToString());
}

@ -3,11 +3,14 @@
@Html.LoadUserManagementAssets()
<div id="wrapper" class="toggled" ng-controller="userManagementController" ng-init="init()" ng-cloak>
<span us-spinner="{radius:30, width:8, length: 16}"></span>
<b>Spinner Active: </b>
{{spinnerActive}}
<!--Sidebar-->
<div id="sidebar-wrapper" class="shadow">
<br />
<br />
<img ng-show="selectedUser.plexInfo.thumb" class="col-md-pull-1 img-circle" style="position: absolute" ng-src="{{selectedUser.plexInfo.thumb}}" />
<br/>
<br/>
<img ng-show="selectedUser.plexInfo.thumb" class="col-md-pull-1 img-circle" style="position: absolute" ng-src="{{selectedUser.plexInfo.thumb}}"/>
<div hidden="hidden" ng-bind="selectedUser.id"></div>
<div>
<strong>Username: </strong><span ng-bind="selectedUser.username"></span>
@ -18,33 +21,33 @@
<div>
<strong>User Type: </strong><span ng-bind="selectedUser.type === 1 ? 'Local User' : 'Plex User'"></span>
</div>
<br />
<br />
<br/>
<br/>
<div ng-show="selectedUser">
<!--Edit-->
<strong>Modify Permissions:</strong>
<!--Load all permissions-->
<div class="checkbox" ng-repeat="p in selectedUser.permissions">
<input id="permissionsCheckbox_{{$id}}" class="checkbox-custom" name="permissions[]" ng-checked="p.selected" ng-model="p.selected" type="checkbox" value="{{p.value}}" />
<input id="permissionsCheckbox_{{$id}}" class="checkbox-custom" name="permissions[]" ng-checked="p.selected" ng-model="p.selected" type="checkbox" value="{{p.value}}"/>
<label for="permissionsCheckbox_{{$id}}">{{p.name}}</label>
</div>
<strong>Modify Features:</strong>
<!--Load all features-->
<div class="checkbox" ng-repeat="p in selectedUser.features">
<input id="featuresCheckbox_{{$id}}" class="checkbox-custom" name="features[]" ng-checked="p.selected" ng-model="p.selected" type="checkbox" value="{{p.value}}" />
<input id="featuresCheckbox_{{$id}}" class="checkbox-custom" name="features[]" ng-checked="p.selected" ng-model="p.selected" type="checkbox" value="{{p.value}}"/>
<label for="featuresCheckbox_{{$id}}">{{p.name}}</label>
</div>
<strong>Email Address</strong>
<div class="form-group">
<input id="emailAddress" type="email" ng-model="selectedUser.emailAddress" ng-disabled="selectedUser.type === 0" class="form-control form-control-custom" />
<input id="emailAddress" type="email" ng-model="selectedUser.emailAddress" ng-disabled="selectedUser.type === 0" class="form-control form-control-custom"/>
</div>
<strong>Alias</strong>
<div class="form-group">
<input id="alias" type="text" ng-model="selectedUser.alias" class="form-control form-control-custom" />
<input id="alias" type="text" ng-model="selectedUser.alias" class="form-control form-control-custom"/>
</div>
@ -59,27 +62,27 @@
<div>
<div class="container-fluid">
<div>
<br />
<br />
<br />
<br/>
<br/>
<br/>
<div class="col-md-12">
<br>
<br>
<div ng-show="error.error" ng-bind="error.errorMessage"></div>
<form name="userform" ng-submit="addUser()" novalidate>
<div class="form-group">
<input id="username" type="text" placeholder="user" ng-model="user.username" class="form-control form-control-custom" />
<input id="username" type="text" placeholder="user" ng-model="user.username" class="form-control form-control-custom"/>
</div>
<div class="form-group">
<input id="password" type="password" placeholder="password" ng-model="user.password" class="form-control form-control-custom" />
<input id="password" type="password" placeholder="password" ng-model="user.password" class="form-control form-control-custom"/>
</div>
<div class="form-group">
<input id="email" type="email" placeholder="email address" ng-model="user.email" class="form-control form-control-custom" />
<input id="email" type="email" placeholder="email address" ng-model="user.email" class="form-control form-control-custom"/>
</div>
<h3>Permissions: </h3>
<div class="checkbox" ng-repeat="permission in permissions">
<input id="permission_{{$id}}" class="checkbox-custom" name="permission[]"
ng-checked="permission.selected" ng-model="permission.selected" type="checkbox" value="{{permission.value}}" />
ng-checked="permission.selected" ng-model="permission.selected" type="checkbox" value="{{permission.value}}"/>
<label for="permission_{{$id}}">{{permission.name}}</label>
</div>
@ -87,13 +90,13 @@
<h3>Features: </h3>
<div class="checkbox" ng-repeat="f in features">
<input id="features_{{$id}}" class="checkbox-custom" name="f[]"
ng-checked="f.selected" ng-model="f.selected" type="checkbox" value="{{f.value}}" />
ng-checked="f.selected" ng-model="f.selected" type="checkbox" value="{{f.value}}"/>
<label for="features_{{$id}}">{{f.name}}</label>
</div>
<input type="submit" class="btn btn-success-outline" value="Add" />
<input type="submit" class="btn btn-success-outline" value="Add"/>
</form>
<form>
@ -111,70 +114,70 @@
<table class="table table-striped table-hover table-responsive table-condensed">
<thead>
<tr>
<th>
<a href="#" ng-click="sortType = 'username'; sortReverse = !sortReverse">
Username
<span ng-show="sortType == 'username' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'username' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th>
<a href="#" ng-click="sortType = 'alias'; sortReverse = !sortReverse">
Alias
<span ng-show="sortType == 'alias' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'alias' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th>
<a href="#" ng-click="sortType = 'emailAddress'; sortReverse = !sortReverse">
Email
<span ng-show="sortType == 'emailAddress' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'emailAddress' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th>
Permissions
</th>
<th>
<a href="#" ng-click="sortType = 'type'; sortReverse = !sortReverse">
User Type
<span ng-show="sortType == 'type' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'type' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th>
<a href="#" ng-click="sortType = 'lastLoggedIn'; sortReverse = !sortReverse">
Last Logged In
<span ng-show="sortType == 'lastLoggedIn' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'lastLoggedIn' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
</tr>
<tr>
<th>
<a href="#" ng-click="sortType = 'username'; sortReverse = !sortReverse">
Username
<span ng-show="sortType == 'username' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'username' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th>
<a href="#" ng-click="sortType = 'alias'; sortReverse = !sortReverse">
Alias
<span ng-show="sortType == 'alias' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'alias' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th>
<a href="#" ng-click="sortType = 'emailAddress'; sortReverse = !sortReverse">
Email
<span ng-show="sortType == 'emailAddress' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'emailAddress' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th>
Permissions
</th>
<th>
<a href="#" ng-click="sortType = 'type'; sortReverse = !sortReverse">
User Type
<span ng-show="sortType == 'type' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'type' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th>
<a href="#" ng-click="sortType = 'lastLoggedIn'; sortReverse = !sortReverse">
Last Logged In
<span ng-show="sortType == 'lastLoggedIn' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'lastLoggedIn' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="u in users | orderBy:sortType:sortReverse | filter:searchTerm">
<td>
{{u.username}}
</td>
<td>
{{u.alias}}
</td>
<td>
{{u.emailAddress}}
</td>
<td>
{{u.permissionsFormattedString}}
</td>
<td>
{{u.type === 1 ? 'Local User' : 'Plex User'}}
</td>
<td ng-bind="u.lastLoggedIn === minDate ? 'Never' : formatDate(u.lastLoggedIn)"></td>
<td>
<a href="#" ng-click="selectUser(u.id)" class="btn btn-sm btn-info-outline detailsBtn">Details/Edit</a>
</td>
</tr>
<tr ng-repeat="u in users | orderBy:sortType:sortReverse | filter:searchTerm">
<td>
{{u.username}}
</td>
<td>
{{u.alias}}
</td>
<td>
{{u.emailAddress}}
</td>
<td>
{{u.permissionsFormattedString}}
</td>
<td>
{{u.type === 1 ? 'Local User' : 'Plex User'}}
</td>
<td ng-bind="u.lastLoggedIn === minDate ? 'Never' : formatDate(u.lastLoggedIn)"></td>
<td>
<a href="#" ng-click="selectUser(u.id)" class="btn btn-sm btn-info-outline detailsBtn">Details/Edit</a>
</td>
</tr>
</tbody>
</table>
</div>

Loading…
Cancel
Save