mirror of https://github.com/abpframework/abp.git
316 changed files with 398620 additions and 221 deletions
@ -0,0 +1,9 @@ |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.MultiTenancy; |
|||
|
|||
public interface IMultiTenantUrlProvider |
|||
{ |
|||
Task<string> GetUrlAsync(string templateUrl); |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.MultiTenancy; |
|||
|
|||
public class MultiTenantUrlProvider : IMultiTenantUrlProvider , ITransientDependency |
|||
{ |
|||
public const string TenantPlaceHolder = "{0}"; |
|||
public const string TenantIdPlaceHolder = "{{tenantId}}"; |
|||
public const string TenantNamePlaceHolder = "{{tenantName}}"; |
|||
|
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected ITenantStore TenantStore { get; } |
|||
|
|||
public MultiTenantUrlProvider( |
|||
ICurrentTenant currentTenant, |
|||
ITenantStore tenantStore) |
|||
{ |
|||
CurrentTenant = currentTenant; |
|||
TenantStore = tenantStore; |
|||
} |
|||
|
|||
public virtual async Task<string> GetUrlAsync(string templateUrl) |
|||
{ |
|||
return await ReplacePlaceHoldersAsync(templateUrl); |
|||
} |
|||
|
|||
protected virtual async Task<string> ReplacePlaceHoldersAsync(string templateUrl) |
|||
{ |
|||
templateUrl = await ReplacePlaceHolderAsync(templateUrl, TenantIdPlaceHolder); |
|||
|
|||
templateUrl = await ReplacePlaceHolderAsync(templateUrl, TenantNamePlaceHolder); |
|||
|
|||
templateUrl = await ReplacePlaceHolderAsync(templateUrl, TenantPlaceHolder); |
|||
|
|||
return templateUrl; |
|||
} |
|||
|
|||
protected virtual async Task<string> ReplacePlaceHolderAsync(string templateUrl, string placeHolder) |
|||
{ |
|||
if (!templateUrl.Contains(placeHolder)) |
|||
{ |
|||
return templateUrl; |
|||
} |
|||
|
|||
var placeHolderValue = string.Empty; |
|||
if (CurrentTenant.IsAvailable) |
|||
{ |
|||
placeHolderValue = (placeHolder == TenantIdPlaceHolder ? CurrentTenant.Id!.Value.ToString() : await GetCurrentTenantNameAsync()) + "."; |
|||
} |
|||
|
|||
if (templateUrl.Contains(placeHolder + '.')) |
|||
{ |
|||
placeHolder += '.'; |
|||
} |
|||
|
|||
templateUrl = templateUrl.Replace( |
|||
placeHolder, |
|||
placeHolderValue |
|||
); |
|||
|
|||
return templateUrl; |
|||
} |
|||
|
|||
protected virtual async Task<string> GetCurrentTenantNameAsync() |
|||
{ |
|||
if (CurrentTenant.Id.HasValue && CurrentTenant.Name.IsNullOrEmpty()) |
|||
{ |
|||
var tenantConfiguration = await TenantStore.FindAsync(CurrentTenant.Id.Value); |
|||
return tenantConfiguration!.Name; |
|||
} |
|||
|
|||
return CurrentTenant.Name!; |
|||
} |
|||
} |
|||
@ -1,25 +1,54 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace Volo.Abp.Http.Client; |
|||
|
|||
public class RemoteServiceConfigurationProvider : IRemoteServiceConfigurationProvider, IScopedDependency |
|||
{ |
|||
protected AbpRemoteServiceOptions Options { get; } |
|||
protected IMultiTenantUrlProvider MultiTenantUrlProvider { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
|
|||
public RemoteServiceConfigurationProvider(IOptionsMonitor<AbpRemoteServiceOptions> options) |
|||
public RemoteServiceConfigurationProvider( |
|||
IOptionsMonitor<AbpRemoteServiceOptions> options, |
|||
IMultiTenantUrlProvider multiTenantUrlProvider, |
|||
ICurrentTenant currentTenant) |
|||
{ |
|||
MultiTenantUrlProvider = multiTenantUrlProvider; |
|||
CurrentTenant = currentTenant; |
|||
Options = options.CurrentValue; |
|||
} |
|||
|
|||
public Task<RemoteServiceConfiguration> GetConfigurationOrDefaultAsync(string name) |
|||
public virtual async Task<RemoteServiceConfiguration> GetConfigurationOrDefaultAsync(string name) |
|||
{ |
|||
return Task.FromResult(Options.RemoteServices.GetConfigurationOrDefault(name)); |
|||
return (await GetMultiTenantConfigurationAsync(Options.RemoteServices.GetConfigurationOrDefault(name)))!; |
|||
} |
|||
|
|||
public Task<RemoteServiceConfiguration?> GetConfigurationOrDefaultOrNullAsync(string name) |
|||
public virtual async Task<RemoteServiceConfiguration?> GetConfigurationOrDefaultOrNullAsync(string name) |
|||
{ |
|||
return Task.FromResult(Options.RemoteServices.GetConfigurationOrDefaultOrNull(name)); |
|||
return await GetMultiTenantConfigurationAsync(Options.RemoteServices.GetConfigurationOrDefaultOrNull(name)); |
|||
} |
|||
|
|||
protected virtual async Task<RemoteServiceConfiguration?> GetMultiTenantConfigurationAsync(RemoteServiceConfiguration? configuration) |
|||
{ |
|||
if (configuration == null) |
|||
{ |
|||
return configuration; |
|||
} |
|||
|
|||
var baseUrl = await MultiTenantUrlProvider.GetUrlAsync(configuration.BaseUrl); |
|||
if (baseUrl == configuration.BaseUrl) |
|||
{ |
|||
return configuration; |
|||
} |
|||
|
|||
var multiTenantConfiguration = new RemoteServiceConfiguration(configuration) |
|||
{ |
|||
BaseUrl = baseUrl |
|||
}; |
|||
|
|||
return multiTenantConfiguration; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,57 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Volo.Abp.MultiTenancy.ConfigurationStore; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.MultiTenancy; |
|||
|
|||
public class MultiTenantUrlProivder_Tests : MultiTenancyTestBase |
|||
{ |
|||
private readonly ICurrentTenant _currentTenant; |
|||
private readonly IMultiTenantUrlProvider _multiTenantUrlProvider; |
|||
|
|||
private readonly Guid _tenantAId = Guid.NewGuid(); |
|||
|
|||
public MultiTenantUrlProivder_Tests() |
|||
{ |
|||
_currentTenant = ServiceProvider.GetRequiredService<ICurrentTenant>(); |
|||
_multiTenantUrlProvider = ServiceProvider.GetRequiredService<IMultiTenantUrlProvider>(); |
|||
} |
|||
protected override void BeforeAddApplication(IServiceCollection services) |
|||
{ |
|||
services.Configure<AbpDefaultTenantStoreOptions>(options => |
|||
{ |
|||
options.Tenants = |
|||
[ |
|||
new TenantConfiguration(_tenantAId, "TenantA") |
|||
]; |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetUrlAsync() |
|||
{ |
|||
var tenantNameHolderUrl = "https://{{tenantName}}.abp.io"; |
|||
var tenantIdHolderUrl = "https://{{tenantId}}.abp.io"; |
|||
var tenantHolderUrl = "https://{0}.abp.io"; |
|||
var hostUrl = "https://abp.io"; |
|||
|
|||
_currentTenant.Id.ShouldBeNull(); |
|||
(await _multiTenantUrlProvider.GetUrlAsync(tenantNameHolderUrl)).ShouldBe(hostUrl); |
|||
(await _multiTenantUrlProvider.GetUrlAsync(tenantIdHolderUrl)).ShouldBe(hostUrl); |
|||
(await _multiTenantUrlProvider.GetUrlAsync(tenantHolderUrl)).ShouldBe(hostUrl); |
|||
(await _multiTenantUrlProvider.GetUrlAsync(hostUrl)).ShouldBe(hostUrl); |
|||
|
|||
using (_currentTenant.Change(_tenantAId)) |
|||
{ |
|||
_currentTenant.Id.ShouldBe(_tenantAId); |
|||
(await _multiTenantUrlProvider.GetUrlAsync(tenantNameHolderUrl)).ShouldBe("https://TenantA.abp.io"); |
|||
(await _multiTenantUrlProvider.GetUrlAsync(tenantIdHolderUrl)).ShouldBe("https://"+_tenantAId+".abp.io"); |
|||
(await _multiTenantUrlProvider.GetUrlAsync(tenantHolderUrl)).ShouldBe("https://TenantA.abp.io"); |
|||
(await _multiTenantUrlProvider.GetUrlAsync(hostUrl)).ShouldBe(hostUrl); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.test.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net8.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.RemoteServices\Volo.Abp.RemoteServices.csproj" /> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.MultiTenancy\Volo.Abp.MultiTenancy.csproj" /> |
|||
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
using Volo.Abp.Testing; |
|||
|
|||
namespace Volo.Abp.Http.Client; |
|||
|
|||
public abstract class AbpRemoteServicesTestBase : AbpIntegratedTest<AbpRemoteServicesTestModule> |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.RemoteServices; |
|||
|
|||
namespace Volo.Abp.Http.Client; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpMultiTenancyModule), |
|||
typeof(AbpRemoteServicesModule) |
|||
)] |
|||
public class AbpRemoteServicesTestModule: AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.MultiTenancy.ConfigurationStore; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Http.Client; |
|||
|
|||
public class RemoteServiceConfigurationProvider_Tests : AbpRemoteServicesTestBase |
|||
{ |
|||
private readonly ICurrentTenant _currentTenant; |
|||
private readonly IRemoteServiceConfigurationProvider _remoteServiceConfigurationProvider; |
|||
private readonly Guid _tenantAId = Guid.NewGuid(); |
|||
|
|||
public RemoteServiceConfigurationProvider_Tests() |
|||
{ |
|||
_currentTenant = GetRequiredService<ICurrentTenant>(); |
|||
_remoteServiceConfigurationProvider = GetRequiredService<IRemoteServiceConfigurationProvider>(); |
|||
} |
|||
|
|||
protected override void AfterAddApplication(IServiceCollection services) |
|||
{ |
|||
services.Configure<AbpRemoteServiceOptions>(options => |
|||
{ |
|||
options.RemoteServices[RemoteServiceConfigurationDictionary.DefaultName] = new RemoteServiceConfiguration("https://abp.io"); |
|||
options.RemoteServices["Identity"] = new RemoteServiceConfiguration("https://{{tenantName}}.abp.io"); |
|||
options.RemoteServices["Permission"] = new RemoteServiceConfiguration("https://{{tenantId}}.abp.io"); |
|||
options.RemoteServices["Setting"] = new RemoteServiceConfiguration("https://{0}.abp.io"); |
|||
}); |
|||
|
|||
services.Configure<AbpDefaultTenantStoreOptions>(options => |
|||
{ |
|||
options.Tenants = |
|||
[ |
|||
new TenantConfiguration(_tenantAId, "TenantA") |
|||
]; |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetConfigurationOrDefaultAsync() |
|||
{ |
|||
_currentTenant.Id.ShouldBeNull(); |
|||
|
|||
var defaultConfiguration = await _remoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(RemoteServiceConfigurationDictionary.DefaultName); |
|||
defaultConfiguration.BaseUrl.ShouldBe("https://abp.io"); |
|||
|
|||
var identityConfiguration = await _remoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync("Identity"); |
|||
identityConfiguration.BaseUrl.ShouldBe("https://abp.io"); |
|||
|
|||
var permissionConfiguration = await _remoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync("Permission"); |
|||
permissionConfiguration.BaseUrl.ShouldBe("https://abp.io"); |
|||
|
|||
var settingConfiguration = await _remoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync("Setting"); |
|||
settingConfiguration.BaseUrl.ShouldBe("https://abp.io"); |
|||
|
|||
using (_currentTenant.Change(_tenantAId, "TenantA")) |
|||
{ |
|||
defaultConfiguration = await _remoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(RemoteServiceConfigurationDictionary.DefaultName); |
|||
defaultConfiguration.BaseUrl.ShouldBe("https://abp.io"); |
|||
|
|||
identityConfiguration = await _remoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync("Identity"); |
|||
identityConfiguration.BaseUrl.ShouldBe($"https://{_currentTenant.Name}.abp.io"); |
|||
|
|||
permissionConfiguration = await _remoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync("Permission"); |
|||
permissionConfiguration.BaseUrl.ShouldBe($"https://{_currentTenant.Id}.abp.io"); |
|||
|
|||
settingConfiguration = await _remoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync("Setting"); |
|||
settingConfiguration.BaseUrl.ShouldBe($"https://{_currentTenant.Name}.abp.io"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
import { AuthErrorEvent, AuthErrorFilter } from '../models'; |
|||
|
|||
export abstract class AbstractAuthErrorFilter<T, E> { |
|||
abstract get(id: string): T; |
|||
abstract add(filter: T): void; |
|||
abstract patch(item: Partial<T>): void; |
|||
abstract remove(id: string): void; |
|||
abstract run(event: E): boolean; |
|||
} |
|||
|
|||
export class AuthErrorFilterService< |
|||
T = AuthErrorFilter, |
|||
E = AuthErrorEvent, |
|||
> extends AbstractAuthErrorFilter<T, E> { |
|||
private warningMessage() { |
|||
console.error('You should add @abp/ng-oauth packages or create your own auth packages.'); |
|||
} |
|||
|
|||
get(id: string): T { |
|||
this.warningMessage(); |
|||
throw new Error('not implemented'); |
|||
} |
|||
|
|||
add(filter: T): void { |
|||
this.warningMessage(); |
|||
} |
|||
|
|||
patch(item: Partial<T>): void { |
|||
this.warningMessage(); |
|||
} |
|||
|
|||
remove(id: string): void { |
|||
this.warningMessage(); |
|||
} |
|||
|
|||
run(event: E): boolean { |
|||
this.warningMessage(); |
|||
throw new Error('not implemented'); |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
export type EventType = |
|||
| 'discovery_document_loaded' |
|||
| 'jwks_load_error' |
|||
| 'invalid_nonce_in_state' |
|||
| 'discovery_document_load_error' |
|||
| 'discovery_document_validation_error' |
|||
| 'user_profile_loaded' |
|||
| 'user_profile_load_error' |
|||
| 'token_received' |
|||
| 'token_error' |
|||
| 'code_error' |
|||
| 'token_refreshed' |
|||
| 'token_refresh_error' |
|||
| 'silent_refresh_error' |
|||
| 'silently_refreshed' |
|||
| 'silent_refresh_timeout' |
|||
| 'token_validation_error' |
|||
| 'token_expires' |
|||
| 'session_changed' |
|||
| 'session_error' |
|||
| 'session_terminated' |
|||
| 'session_unchanged' |
|||
| 'logout' |
|||
| 'popup_closed' |
|||
| 'popup_blocked' |
|||
| 'token_revoke_error'; |
|||
|
|||
export abstract class AuthEvent { |
|||
constructor(public readonly type: EventType) { |
|||
this.type = type; |
|||
} |
|||
} |
|||
|
|||
export class AuthSuccessEvent extends AuthEvent { |
|||
constructor(public readonly type: EventType, public readonly info?: any) { |
|||
super(type); |
|||
} |
|||
} |
|||
|
|||
export class AuthInfoEvent extends AuthEvent { |
|||
constructor(public readonly type: EventType, public readonly info?: any) { |
|||
super(type); |
|||
} |
|||
} |
|||
|
|||
export class AuthErrorEvent extends AuthEvent { |
|||
constructor( |
|||
public readonly type: EventType, |
|||
public readonly reason: object, |
|||
public readonly params?: object, |
|||
) { |
|||
super(type); |
|||
} |
|||
} |
|||
@ -1 +1,2 @@ |
|||
export * from './oauth.service'; |
|||
export * from './oauth-error-filter.service'; |
|||
|
|||
@ -0,0 +1,45 @@ |
|||
import { Injectable, signal } from '@angular/core'; |
|||
import { OAuthErrorEvent } from 'angular-oauth2-oidc'; |
|||
import { AbstractAuthErrorFilter, AuthErrorFilter } from '@abp/ng.core'; |
|||
|
|||
@Injectable({ providedIn: 'root' }) |
|||
export class OAuthErrorFilterService extends AbstractAuthErrorFilter< |
|||
AuthErrorFilter<OAuthErrorEvent>, |
|||
OAuthErrorEvent |
|||
> { |
|||
protected readonly _filters = signal<Array<AuthErrorFilter<OAuthErrorEvent>>>([]); |
|||
readonly filters = this._filters.asReadonly(); |
|||
|
|||
get(id: string): AuthErrorFilter<OAuthErrorEvent> { |
|||
return this._filters().find(({ id: _id }) => _id === id); |
|||
} |
|||
|
|||
add(filter: AuthErrorFilter<OAuthErrorEvent>): void { |
|||
this._filters.update(items => [...items, filter]); |
|||
} |
|||
|
|||
patch(item: Partial<AuthErrorFilter<OAuthErrorEvent>>): void { |
|||
const _item = this.filters().find(({ id }) => id === item.id); |
|||
if (!_item) { |
|||
return; |
|||
} |
|||
|
|||
Object.assign(_item, item); |
|||
} |
|||
|
|||
remove(id: string): void { |
|||
const item = this.filters().find(({ id: _id }) => _id === id); |
|||
if (!item) { |
|||
return; |
|||
} |
|||
|
|||
this._filters.update(items => items.filter(({ id: _id }) => _id !== id)); |
|||
} |
|||
|
|||
run(event: OAuthErrorEvent): boolean { |
|||
return this.filters() |
|||
.filter(({ executable }) => !!executable) |
|||
.map(({ execute }) => execute(event)) |
|||
.some(item => item); |
|||
} |
|||
} |
|||
@ -1,21 +1,11 @@ |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using AbpPerfTest.WithAbp; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
|
|||
namespace AbpPerfTest.WithAbp |
|||
{ |
|||
public class Program |
|||
{ |
|||
public static void Main(string[] args) |
|||
{ |
|||
CreateHostBuilder(args).Build().Run(); |
|||
} |
|||
|
|||
public static IHostBuilder CreateHostBuilder(string[] args) => |
|||
Host.CreateDefaultBuilder(args) |
|||
.ConfigureWebHostDefaults(webBuilder => |
|||
{ |
|||
webBuilder.UseStartup<Startup>(); |
|||
}) |
|||
.UseAutofac(); |
|||
} |
|||
} |
|||
var builder = WebApplication.CreateBuilder(args); |
|||
builder.Host.UseAutofac(); |
|||
await builder.AddApplicationAsync<AppModule>(); |
|||
var app = builder.Build(); |
|||
await app.InitializeApplicationAsync(); |
|||
await app.RunAsync(); |
|||
|
|||
@ -1,18 +0,0 @@ |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
|
|||
namespace AbpPerfTest.WithAbp |
|||
{ |
|||
public class Startup |
|||
{ |
|||
public void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddApplication<AppModule>(); |
|||
} |
|||
|
|||
public void Configure(IApplicationBuilder app) |
|||
{ |
|||
app.InitializeApplication(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,17 +1,31 @@ |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using AbpPerfTest.WithoutAbp.EntityFramework; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
|
|||
namespace AbpPerfTest.WithoutAbp |
|||
var builder = WebApplication.CreateBuilder(args); |
|||
|
|||
builder.Services.AddMvc(); |
|||
|
|||
builder.Services.AddDbContext<BookDbContext>(options => |
|||
{ |
|||
options.UseSqlServer(builder.Configuration.GetConnectionString("Default")); |
|||
}); |
|||
|
|||
var app = builder.Build(); |
|||
|
|||
if (builder.Environment.IsDevelopment()) |
|||
{ |
|||
public class Program |
|||
{ |
|||
public static void Main(string[] args) |
|||
{ |
|||
CreateHostBuilder(args).Build().Run(); |
|||
} |
|||
|
|||
public static IHostBuilder CreateHostBuilder(string[] args) => |
|||
Host.CreateDefaultBuilder(args) |
|||
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); |
|||
} |
|||
app.UseDeveloperExceptionPage(); |
|||
} |
|||
|
|||
app.UseRouting(); |
|||
|
|||
app.UseEndpoints(endpoints => |
|||
{ |
|||
endpoints.MapControllers(); |
|||
}); |
|||
|
|||
app.Run(); |
|||
|
|||
@ -1,45 +0,0 @@ |
|||
using AbpPerfTest.WithoutAbp.EntityFramework; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
|
|||
namespace AbpPerfTest.WithoutAbp |
|||
{ |
|||
public class Startup |
|||
{ |
|||
private readonly IConfiguration _configuration; |
|||
|
|||
public Startup(IConfiguration configuration) |
|||
{ |
|||
_configuration = configuration; |
|||
} |
|||
|
|||
public void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddMvc(); |
|||
|
|||
services.AddDbContext<BookDbContext>(options => |
|||
{ |
|||
options.UseSqlServer(_configuration.GetConnectionString("Default")); |
|||
}); |
|||
} |
|||
|
|||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) |
|||
{ |
|||
if (env.IsDevelopment()) |
|||
{ |
|||
app.UseDeveloperExceptionPage(); |
|||
} |
|||
|
|||
app.UseRouting(); |
|||
|
|||
app.UseEndpoints(endpoints => |
|||
{ |
|||
endpoints.MapControllers(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,48 @@ |
|||
/* |
|||
Licensed to the Apache Software Foundation (ASF) under one or more |
|||
contributor license agreements. See the NOTICE file distributed with |
|||
this work for additional information regarding copyright ownership. |
|||
The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
(the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
*/ |
|||
#stats td, th |
|||
{ |
|||
|
|||
width: 9%;//140px; |
|||
height: 30px; |
|||
} |
|||
#stats td |
|||
{ |
|||
text-align: center; |
|||
} |
|||
#stats th |
|||
{ |
|||
font-weight:bold; |
|||
} |
|||
.blue |
|||
{ |
|||
background-color: #dff0d8; |
|||
} |
|||
#errors td, th |
|||
{ |
|||
text-align: center; |
|||
width: 400px; |
|||
height: 30px; |
|||
} |
|||
#errors td |
|||
{ |
|||
|
|||
} |
|||
#errors th |
|||
{ |
|||
font-weight:bold; |
|||
} |
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,61 @@ |
|||
/* |
|||
Licensed to the Apache Software Foundation (ASF) under one or more |
|||
contributor license agreements. See the NOTICE file distributed with |
|||
this work for additional information regarding copyright ownership. |
|||
The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
(the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
*/ |
|||
.legend { |
|||
margin-left: -40px; |
|||
font-size: 12px; |
|||
display: inline; |
|||
font-family: 'Open Sans'; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.panel-footer |
|||
{ |
|||
|
|||
} |
|||
|
|||
.subLegend |
|||
{ |
|||
|
|||
} |
|||
|
|||
.legend li > div { |
|||
display: inline-block; |
|||
margin-right: 3px; |
|||
margin-left: 5px; |
|||
} |
|||
|
|||
.legend li label { |
|||
margin-left: 1px; |
|||
margin-right: 10px; |
|||
color: black; |
|||
font-family: Open sans; |
|||
/*width: 200px;*/ |
|||
} |
|||
li |
|||
{ |
|||
list-style-type:none; |
|||
display: inline-block; |
|||
} |
|||
|
|||
.nav-third-level |
|||
{ |
|||
font-size: 12px; |
|||
} |
|||
|
|||
.legend-disabled { |
|||
border-color: #818181 !important; |
|||
} |
|||
@ -0,0 +1,233 @@ |
|||
/************* |
|||
Blue Theme |
|||
*************/ |
|||
/* overall */ |
|||
.tablesorter-blue { |
|||
width: 100%; |
|||
background-color: #fff; |
|||
margin: 10px 0 15px; |
|||
text-align: left; |
|||
border-spacing: 0; |
|||
border: #cdcdcd 1px solid; |
|||
border-width: 1px 0 0 1px; |
|||
} |
|||
.tablesorter-blue th, |
|||
.tablesorter-blue td { |
|||
border: #cdcdcd 1px solid; |
|||
border-width: 0 1px 1px 0; |
|||
} |
|||
|
|||
/* header */ |
|||
.tablesorter-blue th, |
|||
.tablesorter-blue thead td { |
|||
font: 12px/18px Arial, Sans-serif; |
|||
font-weight: bold; |
|||
color: #000; |
|||
background-color: #99bfe6; |
|||
border-collapse: collapse; |
|||
padding: 4px; |
|||
text-shadow: 0 1px 0 rgba(204, 204, 204, 0.7); |
|||
} |
|||
.tablesorter-blue tbody td, |
|||
.tablesorter-blue tfoot th, |
|||
.tablesorter-blue tfoot td { |
|||
padding: 4px; |
|||
vertical-align: top; |
|||
} |
|||
.tablesorter-blue .header, |
|||
.tablesorter-blue .tablesorter-header { |
|||
/* black (unsorted) double arrow */ |
|||
background-image: url(data:image/gif;base64,R0lGODlhFQAJAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==); |
|||
/* white (unsorted) double arrow */ |
|||
/* background-image: url(data:image/gif;base64,R0lGODlhFQAJAIAAAP///////yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==); */ |
|||
/* image */ |
|||
/* background-image: url(images/black-unsorted.gif); */ |
|||
background-repeat: no-repeat; |
|||
background-position: center right; |
|||
padding: 4px 18px 4px 4px; |
|||
white-space: normal; |
|||
cursor: pointer; |
|||
} |
|||
.tablesorter-blue .headerSortUp, |
|||
.tablesorter-blue .tablesorter-headerSortUp, |
|||
.tablesorter-blue .tablesorter-headerAsc { |
|||
background-color: #9fbfdf; |
|||
/* black asc arrow */ |
|||
background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7); |
|||
/* white asc arrow */ |
|||
/* background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7); */ |
|||
/* image */ |
|||
/* background-image: url(images/black-asc.gif); */ |
|||
} |
|||
.tablesorter-blue .headerSortDown, |
|||
.tablesorter-blue .tablesorter-headerSortDown, |
|||
.tablesorter-blue .tablesorter-headerDesc { |
|||
background-color: #8cb3d9; |
|||
/* black desc arrow */ |
|||
background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7); |
|||
/* white desc arrow */ |
|||
/* background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7); */ |
|||
/* image */ |
|||
/* background-image: url(images/black-desc.gif); */ |
|||
} |
|||
.tablesorter-blue thead .sorter-false { |
|||
background-image: none; |
|||
cursor: default; |
|||
padding: 4px; |
|||
} |
|||
|
|||
/* tfoot */ |
|||
.tablesorter-blue tfoot .tablesorter-headerSortUp, |
|||
.tablesorter-blue tfoot .tablesorter-headerSortDown, |
|||
.tablesorter-blue tfoot .tablesorter-headerAsc, |
|||
.tablesorter-blue tfoot .tablesorter-headerDesc { |
|||
/* remove sort arrows from footer */ |
|||
background-image: none; |
|||
} |
|||
|
|||
/* tbody */ |
|||
.tablesorter-blue td { |
|||
color: #3d3d3d; |
|||
background-color: #fff; |
|||
padding: 4px; |
|||
vertical-align: top; |
|||
} |
|||
|
|||
/* hovered row colors |
|||
you'll need to add additional lines for |
|||
rows with more than 2 child rows |
|||
*/ |
|||
.tablesorter-blue tbody > tr.hover > td, |
|||
.tablesorter-blue tbody > tr:hover > td, |
|||
.tablesorter-blue tbody > tr:hover + tr.tablesorter-childRow > td, |
|||
.tablesorter-blue tbody > tr:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td, |
|||
.tablesorter-blue tbody > tr.even.hover > td, |
|||
.tablesorter-blue tbody > tr.even:hover > td, |
|||
.tablesorter-blue tbody > tr.even:hover + tr.tablesorter-childRow > td, |
|||
.tablesorter-blue tbody > tr.even:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td { |
|||
background-color: #d9d9d9; |
|||
} |
|||
.tablesorter-blue tbody > tr.odd.hover > td, |
|||
.tablesorter-blue tbody > tr.odd:hover > td, |
|||
.tablesorter-blue tbody > tr.odd:hover + tr.tablesorter-childRow > td, |
|||
.tablesorter-blue tbody > tr.odd:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td { |
|||
background-color: #bfbfbf; |
|||
} |
|||
|
|||
/* table processing indicator */ |
|||
.tablesorter-blue .tablesorter-processing { |
|||
background-position: center center !important; |
|||
background-repeat: no-repeat !important; |
|||
/* background-image: url(images/loading.gif) !important; */ |
|||
background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; |
|||
} |
|||
|
|||
/* Zebra Widget - row alternating colors */ |
|||
.tablesorter-blue tbody tr.odd > td { |
|||
background-color: #ebf2fa; |
|||
} |
|||
.tablesorter-blue tbody tr.even > td { |
|||
background-color: #fff; |
|||
} |
|||
|
|||
/* Column Widget - column sort colors */ |
|||
.tablesorter-blue td.primary, |
|||
.tablesorter-blue tr.odd td.primary { |
|||
background-color: #99b3e6; |
|||
} |
|||
.tablesorter-blue tr.even td.primary { |
|||
background-color: #c2d1f0; |
|||
} |
|||
.tablesorter-blue td.secondary, |
|||
.tablesorter-blue tr.odd td.secondary { |
|||
background-color: #c2d1f0; |
|||
} |
|||
.tablesorter-blue tr.even td.secondary { |
|||
background-color: #d6e0f5; |
|||
} |
|||
.tablesorter-blue td.tertiary, |
|||
.tablesorter-blue tr.odd td.tertiary { |
|||
background-color: #d6e0f5; |
|||
} |
|||
.tablesorter-blue tr.even td.tertiary { |
|||
background-color: #ebf0fa; |
|||
} |
|||
|
|||
/* caption */ |
|||
caption { |
|||
background-color: #fff; |
|||
} |
|||
|
|||
/* filter widget */ |
|||
.tablesorter-blue .tablesorter-filter-row { |
|||
background-color: #eee; |
|||
} |
|||
.tablesorter-blue .tablesorter-filter-row td { |
|||
background-color: #eee; |
|||
line-height: normal; |
|||
text-align: center; /* center the input */ |
|||
-webkit-transition: line-height 0.1s ease; |
|||
-moz-transition: line-height 0.1s ease; |
|||
-o-transition: line-height 0.1s ease; |
|||
transition: line-height 0.1s ease; |
|||
} |
|||
/* optional disabled input styling */ |
|||
.tablesorter-blue .tablesorter-filter-row .disabled { |
|||
opacity: 0.5; |
|||
filter: alpha(opacity=50); |
|||
cursor: not-allowed; |
|||
} |
|||
/* hidden filter row */ |
|||
.tablesorter-blue .tablesorter-filter-row.hideme td { |
|||
/*** *********************************************** ***/ |
|||
/*** change this padding to modify the thickness ***/ |
|||
/*** of the closed filter row (height = padding x 2) ***/ |
|||
padding: 2px; |
|||
/*** *********************************************** ***/ |
|||
margin: 0; |
|||
line-height: 0; |
|||
cursor: pointer; |
|||
} |
|||
.tablesorter-blue .tablesorter-filter-row.hideme * { |
|||
height: 1px; |
|||
min-height: 0; |
|||
border: 0; |
|||
padding: 0; |
|||
margin: 0; |
|||
/* don't use visibility: hidden because it disables tabbing */ |
|||
opacity: 0; |
|||
filter: alpha(opacity=0); |
|||
} |
|||
/* filters */ |
|||
.tablesorter-blue input.tablesorter-filter, |
|||
.tablesorter-blue select.tablesorter-filter { |
|||
width: 98%; |
|||
height: auto; |
|||
margin: 0; |
|||
padding: 4px; |
|||
background-color: #fff; |
|||
border: 1px solid #bbb; |
|||
color: #333; |
|||
-webkit-box-sizing: border-box; |
|||
-moz-box-sizing: border-box; |
|||
box-sizing: border-box; |
|||
-webkit-transition: height 0.1s ease; |
|||
-moz-transition: height 0.1s ease; |
|||
-o-transition: height 0.1s ease; |
|||
transition: height 0.1s ease; |
|||
} |
|||
/* rows hidden by filtering (needed for child rows) */ |
|||
.tablesorter .filtered { |
|||
display: none; |
|||
} |
|||
|
|||
/* ajax error row */ |
|||
.tablesorter .tablesorter-errorRow td { |
|||
text-align: center; |
|||
cursor: pointer; |
|||
background-color: #e6bf99; |
|||
} |
|||
|
|||
.tablesorter .tablesorter-no-sort td { |
|||
font-weight: bold; |
|||
} |
|||
@ -0,0 +1,483 @@ |
|||
/* The MIT License |
|||
|
|||
Copyright (c) 2011 by Michael Zinsmaier and nergal.dev |
|||
Copyright (c) 2012 by Thomas Ritou |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in |
|||
all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
THE SOFTWARE. |
|||
*/ |
|||
|
|||
/* |
|||
____________________________________________________ |
|||
|
|||
what it is: |
|||
____________________________________________________ |
|||
|
|||
curvedLines is a plugin for flot, that tries to display lines in a smoother way. |
|||
This is achieved through adding of more data points. The plugin is a data processor and can thus be used |
|||
in combination with standard line / point rendering options. |
|||
|
|||
=> 1) with large data sets you may get trouble |
|||
=> 2) if you want to display the points too, you have to plot them as 2nd data series over the lines |
|||
=> 3) consecutive x data points are not allowed to have the same value |
|||
|
|||
Feel free to further improve the code |
|||
|
|||
____________________________________________________ |
|||
|
|||
how to use it: |
|||
____________________________________________________ |
|||
|
|||
var d1 = [[5,5],[7,3],[9,12]]; |
|||
|
|||
var options = { series: { curvedLines: { active: true }}}; |
|||
|
|||
$.plot($("#placeholder"), [{data: d1, lines: { show: true}, curvedLines: {apply: true}}], options); |
|||
|
|||
_____________________________________________________ |
|||
|
|||
options: |
|||
_____________________________________________________ |
|||
|
|||
active: bool true => plugin can be used |
|||
apply: bool true => series will be drawn as curved line |
|||
monotonicFit: bool true => uses monotone cubic interpolation (preserve monotonicity) |
|||
tension: int defines the tension parameter of the hermite spline interpolation (no effect if monotonicFit is set) |
|||
nrSplinePoints: int defines the number of sample points (of the spline) in between two consecutive points |
|||
|
|||
deprecated options from flot prior to 1.0.0: |
|||
------------------------------------------------ |
|||
legacyOverride bool true => use old default |
|||
OR |
|||
legacyOverride optionArray |
|||
{ |
|||
fit: bool true => forces the max,mins of the curve to be on the datapoints |
|||
curvePointFactor int defines how many "virtual" points are used per "real" data point to |
|||
emulate the curvedLines (points total = real points * curvePointFactor) |
|||
fitPointDist: int defines the x axis distance of the additional two points that are used |
|||
} to enforce the min max condition. |
|||
*/ |
|||
|
|||
/* |
|||
* v0.1 initial commit |
|||
* v0.15 negative values should work now (outcommented a negative -> 0 hook hope it does no harm) |
|||
* v0.2 added fill option (thanks to monemihir) and multi axis support (thanks to soewono effendi) |
|||
* v0.3 improved saddle handling and added basic handling of Dates |
|||
* v0.4 rewritten fill option (thomas ritou) mostly from original flot code (now fill between points rather than to graph bottom), corrected fill Opacity bug |
|||
* v0.5 rewritten instead of implementing a own draw function CurvedLines is now based on the processDatapoints flot hook (credits go to thomas ritou). |
|||
* This change breakes existing code however CurvedLines are now just many tiny straight lines to flot and therefore all flot lines options (like gradient fill, |
|||
* shadow) are now supported out of the box |
|||
* v0.6 flot 0.8 compatibility and some bug fixes |
|||
* v0.6.x changed versioning schema |
|||
* |
|||
* v1.0.0 API Break marked existing implementation/options as deprecated |
|||
* v1.1.0 added the new curved line calculations based on hermite splines |
|||
* v1.1.1 added a rough parameter check to make sure the new options are used |
|||
*/ |
|||
|
|||
(function($) { |
|||
|
|||
var options = { |
|||
series : { |
|||
curvedLines : { |
|||
active : false, |
|||
apply : false, |
|||
monotonicFit : false, |
|||
tension : 0.5, |
|||
nrSplinePoints : 20, |
|||
legacyOverride : undefined |
|||
} |
|||
} |
|||
}; |
|||
|
|||
function init(plot) { |
|||
|
|||
plot.hooks.processOptions.push(processOptions); |
|||
|
|||
//if the plugin is active register processDatapoints method
|
|||
function processOptions(plot, options) { |
|||
if (options.series.curvedLines.active) { |
|||
plot.hooks.processDatapoints.unshift(processDatapoints); |
|||
} |
|||
} |
|||
|
|||
//only if the plugin is active
|
|||
function processDatapoints(plot, series, datapoints) { |
|||
var nrPoints = datapoints.points.length / datapoints.pointsize; |
|||
var EPSILON = 0.005; |
|||
|
|||
//detects missplaced legacy parameters (prior v1.x.x) in the options object
|
|||
//this can happen if somebody upgrades to v1.x.x without adjusting the parameters or uses old examples
|
|||
var invalidLegacyOptions = hasInvalidParameters(series.curvedLines); |
|||
|
|||
if (!invalidLegacyOptions && series.curvedLines.apply == true && series.originSeries === undefined && nrPoints > (1 + EPSILON)) { |
|||
if (series.lines.fill) { |
|||
|
|||
var pointsTop = calculateCurvePoints(datapoints, series.curvedLines, 1); |
|||
var pointsBottom = calculateCurvePoints(datapoints, series.curvedLines, 2); |
|||
//flot makes sure for us that we've got a second y point if fill is true !
|
|||
|
|||
//Merge top and bottom curve
|
|||
datapoints.pointsize = 3; |
|||
datapoints.points = []; |
|||
var j = 0; |
|||
var k = 0; |
|||
var i = 0; |
|||
var ps = 2; |
|||
while (i < pointsTop.length || j < pointsBottom.length) { |
|||
if (pointsTop[i] == pointsBottom[j]) { |
|||
datapoints.points[k] = pointsTop[i]; |
|||
datapoints.points[k + 1] = pointsTop[i + 1]; |
|||
datapoints.points[k + 2] = pointsBottom[j + 1]; |
|||
j += ps; |
|||
i += ps; |
|||
|
|||
} else if (pointsTop[i] < pointsBottom[j]) { |
|||
datapoints.points[k] = pointsTop[i]; |
|||
datapoints.points[k + 1] = pointsTop[i + 1]; |
|||
datapoints.points[k + 2] = k > 0 ? datapoints.points[k - 1] : null; |
|||
i += ps; |
|||
} else { |
|||
datapoints.points[k] = pointsBottom[j]; |
|||
datapoints.points[k + 1] = k > 1 ? datapoints.points[k - 2] : null; |
|||
datapoints.points[k + 2] = pointsBottom[j + 1]; |
|||
j += ps; |
|||
} |
|||
k += 3; |
|||
} |
|||
} else if (series.lines.lineWidth > 0) { |
|||
datapoints.points = calculateCurvePoints(datapoints, series.curvedLines, 1); |
|||
datapoints.pointsize = 2; |
|||
} |
|||
} |
|||
} |
|||
|
|||
function calculateCurvePoints(datapoints, curvedLinesOptions, yPos) { |
|||
if ( typeof curvedLinesOptions.legacyOverride != 'undefined' && curvedLinesOptions.legacyOverride != false) { |
|||
var defaultOptions = { |
|||
fit : false, |
|||
curvePointFactor : 20, |
|||
fitPointDist : undefined |
|||
}; |
|||
var legacyOptions = jQuery.extend(defaultOptions, curvedLinesOptions.legacyOverride); |
|||
return calculateLegacyCurvePoints(datapoints, legacyOptions, yPos); |
|||
} |
|||
|
|||
return calculateSplineCurvePoints(datapoints, curvedLinesOptions, yPos); |
|||
} |
|||
|
|||
function calculateSplineCurvePoints(datapoints, curvedLinesOptions, yPos) { |
|||
var points = datapoints.points; |
|||
var ps = datapoints.pointsize; |
|||
|
|||
//create interpolant fuction
|
|||
var splines = createHermiteSplines(datapoints, curvedLinesOptions, yPos); |
|||
var result = []; |
|||
|
|||
//sample the function
|
|||
// (the result is intependent from the input data =>
|
|||
// it is ok to alter the input after this method)
|
|||
var j = 0; |
|||
for (var i = 0; i < points.length - ps; i += ps) { |
|||
var curX = i; |
|||
var curY = i + yPos; |
|||
|
|||
var xStart = points[curX]; |
|||
var xEnd = points[curX + ps]; |
|||
var xStep = (xEnd - xStart) / Number(curvedLinesOptions.nrSplinePoints); |
|||
|
|||
//add point
|
|||
result.push(points[curX]); |
|||
result.push(points[curY]); |
|||
|
|||
//add curve point
|
|||
for (var x = (xStart += xStep); x < xEnd; x += xStep) { |
|||
result.push(x); |
|||
result.push(splines[j](x)); |
|||
} |
|||
|
|||
j++; |
|||
} |
|||
|
|||
//add last point
|
|||
result.push(points[points.length - ps]); |
|||
result.push(points[points.length - ps + yPos]); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
|
|||
|
|||
// Creates an array of splines, one for each segment of the original curve. Algorithm based on the wikipedia articles:
|
|||
//
|
|||
// http://de.wikipedia.org/w/index.php?title=Kubisch_Hermitescher_Spline&oldid=130168003 and
|
|||
// http://en.wikipedia.org/w/index.php?title=Monotone_cubic_interpolation&oldid=622341725 and the description of Fritsch-Carlson from
|
|||
// http://math.stackexchange.com/questions/45218/implementation-of-monotone-cubic-interpolation
|
|||
// for a detailed description see https://github.com/MichaelZinsmaier/CurvedLines/docu
|
|||
function createHermiteSplines(datapoints, curvedLinesOptions, yPos) { |
|||
var points = datapoints.points; |
|||
var ps = datapoints.pointsize; |
|||
|
|||
// preparation get length (x_{k+1} - x_k) and slope s=(p_{k+1} - p_k) / (x_{k+1} - x_k) of the segments
|
|||
var segmentLengths = []; |
|||
var segmentSlopes = []; |
|||
|
|||
for (var i = 0; i < points.length - ps; i += ps) { |
|||
var curX = i; |
|||
var curY = i + yPos; |
|||
var dx = points[curX + ps] - points[curX]; |
|||
var dy = points[curY + ps] - points[curY]; |
|||
|
|||
segmentLengths.push(dx); |
|||
segmentSlopes.push(dy / dx); |
|||
} |
|||
|
|||
//get the values for the desired gradients m_k for all points k
|
|||
//depending on the used method the formula is different
|
|||
var gradients = [segmentSlopes[0]]; |
|||
if (curvedLinesOptions.monotonicFit) { |
|||
// Fritsch Carlson
|
|||
for (var i = 1; i < segmentLengths.length; i++) { |
|||
var slope = segmentSlopes[i]; |
|||
var prev_slope = segmentSlopes[i - 1]; |
|||
if (slope * prev_slope <= 0) { // sign(prev_slope) != sign(slpe)
|
|||
gradients.push(0); |
|||
} else { |
|||
var length = segmentLengths[i]; |
|||
var prev_length = segmentLengths[i - 1]; |
|||
var common = length + prev_length; |
|||
//m = 3 (prev_length + length) / ((2 length + prev_length) / prev_slope + (length + 2 prev_length) / slope)
|
|||
gradients.push(3 * common / ((common + length) / prev_slope + (common + prev_length) / slope)); |
|||
} |
|||
} |
|||
} else { |
|||
// Cardinal spline with t € [0,1]
|
|||
// Catmull-Rom for t = 0
|
|||
for (var i = ps; i < points.length - ps; i += ps) { |
|||
var curX = i; |
|||
var curY = i + yPos; |
|||
gradients.push(Number(curvedLinesOptions.tension) * (points[curY + ps] - points[curY - ps]) / (points[curX + ps] - points[curX - ps])); |
|||
} |
|||
} |
|||
gradients.push(segmentSlopes[segmentSlopes.length - 1]); |
|||
|
|||
//get the two major coefficients (c'_{oef1} and c'_{oef2}) for each segment spline
|
|||
var coefs1 = []; |
|||
var coefs2 = []; |
|||
for (i = 0; i < segmentLengths.length; i++) { |
|||
var m_k = gradients[i]; |
|||
var m_k_plus = gradients[i + 1]; |
|||
var slope = segmentSlopes[i]; |
|||
var invLength = 1 / segmentLengths[i]; |
|||
var common = m_k + m_k_plus - slope - slope; |
|||
|
|||
coefs1.push(common * invLength * invLength); |
|||
coefs2.push((slope - common - m_k) * invLength); |
|||
} |
|||
|
|||
//create functions with from the coefficients and capture the parameters
|
|||
var ret = []; |
|||
for (var i = 0; i < segmentLengths.length; i ++) { |
|||
var spline = function (x_k, coef1, coef2, coef3, coef4) { |
|||
// spline for a segment
|
|||
return function (x) { |
|||
var diff = x - x_k; |
|||
var diffSq = diff * diff; |
|||
return coef1 * diff * diffSq + coef2 * diffSq + coef3 * diff + coef4; |
|||
}; |
|||
}; |
|||
|
|||
ret.push(spline(points[i * ps], coefs1[i], coefs2[i], gradients[i], points[i * ps + yPos])); |
|||
} |
|||
|
|||
return ret; |
|||
}; |
|||
|
|||
//no real idea whats going on here code mainly from https://code.google.com/p/flot/issues/detail?id=226
|
|||
//if fit option is selected additional datapoints get inserted before the curve calculations in nergal.dev s code.
|
|||
function calculateLegacyCurvePoints(datapoints, curvedLinesOptions, yPos) { |
|||
|
|||
var points = datapoints.points; |
|||
var ps = datapoints.pointsize; |
|||
var num = Number(curvedLinesOptions.curvePointFactor) * (points.length / ps); |
|||
|
|||
var xdata = new Array; |
|||
var ydata = new Array; |
|||
|
|||
var curX = -1; |
|||
var curY = -1; |
|||
var j = 0; |
|||
|
|||
if (curvedLinesOptions.fit) { |
|||
//insert a point before and after the "real" data point to force the line
|
|||
//to have a max,min at the data point.
|
|||
|
|||
var fpDist; |
|||
if ( typeof curvedLinesOptions.fitPointDist == 'undefined') { |
|||
//estimate it
|
|||
var minX = points[0]; |
|||
var maxX = points[points.length - ps]; |
|||
fpDist = (maxX - minX) / (500 * 100); |
|||
//x range / (estimated pixel length of placeholder * factor)
|
|||
} else { |
|||
//use user defined value
|
|||
fpDist = Number(curvedLinesOptions.fitPointDist); |
|||
} |
|||
|
|||
for (var i = 0; i < points.length; i += ps) { |
|||
|
|||
var frontX; |
|||
var backX; |
|||
curX = i; |
|||
curY = i + yPos; |
|||
|
|||
//add point X s
|
|||
frontX = points[curX] - fpDist; |
|||
backX = points[curX] + fpDist; |
|||
|
|||
var factor = 2; |
|||
while (frontX == points[curX] || backX == points[curX]) { |
|||
//inside the ulp
|
|||
frontX = points[curX] - (fpDist * factor); |
|||
backX = points[curX] + (fpDist * factor); |
|||
factor++; |
|||
} |
|||
|
|||
//add curve points
|
|||
xdata[j] = frontX; |
|||
ydata[j] = points[curY]; |
|||
j++; |
|||
|
|||
xdata[j] = points[curX]; |
|||
ydata[j] = points[curY]; |
|||
j++; |
|||
|
|||
xdata[j] = backX; |
|||
ydata[j] = points[curY]; |
|||
j++; |
|||
} |
|||
} else { |
|||
//just use the datapoints
|
|||
for (var i = 0; i < points.length; i += ps) { |
|||
curX = i; |
|||
curY = i + yPos; |
|||
|
|||
xdata[j] = points[curX]; |
|||
ydata[j] = points[curY]; |
|||
j++; |
|||
} |
|||
} |
|||
|
|||
var n = xdata.length; |
|||
|
|||
var y2 = new Array(); |
|||
var delta = new Array(); |
|||
y2[0] = 0; |
|||
y2[n - 1] = 0; |
|||
delta[0] = 0; |
|||
|
|||
for (var i = 1; i < n - 1; ++i) { |
|||
var d = (xdata[i + 1] - xdata[i - 1]); |
|||
if (d == 0) { |
|||
//point before current point and after current point need some space in between
|
|||
return []; |
|||
} |
|||
|
|||
var s = (xdata[i] - xdata[i - 1]) / d; |
|||
var p = s * y2[i - 1] + 2; |
|||
y2[i] = (s - 1) / p; |
|||
delta[i] = (ydata[i + 1] - ydata[i]) / (xdata[i + 1] - xdata[i]) - (ydata[i] - ydata[i - 1]) / (xdata[i] - xdata[i - 1]); |
|||
delta[i] = (6 * delta[i] / (xdata[i + 1] - xdata[i - 1]) - s * delta[i - 1]) / p; |
|||
} |
|||
|
|||
for (var j = n - 2; j >= 0; --j) { |
|||
y2[j] = y2[j] * y2[j + 1] + delta[j]; |
|||
} |
|||
|
|||
// xmax - xmin / #points
|
|||
var step = (xdata[n - 1] - xdata[0]) / (num - 1); |
|||
|
|||
var xnew = new Array; |
|||
var ynew = new Array; |
|||
var result = new Array; |
|||
|
|||
xnew[0] = xdata[0]; |
|||
ynew[0] = ydata[0]; |
|||
|
|||
result.push(xnew[0]); |
|||
result.push(ynew[0]); |
|||
|
|||
for ( j = 1; j < num; ++j) { |
|||
//new x point (sampling point for the created curve)
|
|||
xnew[j] = xnew[0] + j * step; |
|||
|
|||
var max = n - 1; |
|||
var min = 0; |
|||
|
|||
while (max - min > 1) { |
|||
var k = Math.round((max + min) / 2); |
|||
if (xdata[k] > xnew[j]) { |
|||
max = k; |
|||
} else { |
|||
min = k; |
|||
} |
|||
} |
|||
|
|||
//found point one to the left and one to the right of generated new point
|
|||
var h = (xdata[max] - xdata[min]); |
|||
|
|||
if (h == 0) { |
|||
//similar to above two points from original x data need some space between them
|
|||
return []; |
|||
} |
|||
|
|||
var a = (xdata[max] - xnew[j]) / h; |
|||
var b = (xnew[j] - xdata[min]) / h; |
|||
|
|||
ynew[j] = a * ydata[min] + b * ydata[max] + ((a * a * a - a) * y2[min] + (b * b * b - b) * y2[max]) * (h * h) / 6; |
|||
|
|||
result.push(xnew[j]); |
|||
result.push(ynew[j]); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
function hasInvalidParameters(curvedLinesOptions) { |
|||
if (typeof curvedLinesOptions.fit != 'undefined' || |
|||
typeof curvedLinesOptions.curvePointFactor != 'undefined' || |
|||
typeof curvedLinesOptions.fitPointDist != 'undefined') { |
|||
throw new Error("CurvedLines detected illegal parameters. The CurvedLines API changed with version 1.0.0 please check the options object."); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
|
|||
}//end init
|
|||
|
|||
|
|||
$.plot.plugins.push({ |
|||
init : init, |
|||
options : options, |
|||
name : 'curvedLines', |
|||
version : '1.1.1' |
|||
}); |
|||
|
|||
})(jQuery); |
|||
|
|||
@ -0,0 +1,50 @@ |
|||
/* |
|||
Licensed to the Apache Software Foundation (ASF) under one or more |
|||
contributor license agreements. See the NOTICE file distributed with |
|||
this work for additional information regarding copyright ownership. |
|||
The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
(the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0
|
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
*/ |
|||
|
|||
|
|||
|
|||
$(document).ready(function() { |
|||
$(".click-title").mouseenter( function( e){ |
|||
e.preventDefault(); |
|||
this.style.cursor="pointer"; |
|||
}); |
|||
$(".click-title").mousedown( function(event){ |
|||
event.preventDefault(); |
|||
}); |
|||
|
|||
|
|||
$(".portlet-header").css("cursor", "auto"); |
|||
}); |
|||
|
|||
|
|||
function collapse(elem, collapsed){ |
|||
if(collapsed){ |
|||
$(elem).parent().find(".fa-chevron-up").removeClass("fa-chevron-up").addClass("fa-chevron-down"); |
|||
}else{ |
|||
$(elem).parent().find(".fa-chevron-down").removeClass("fa-chevron-down").addClass("fa-chevron-up"); |
|||
|
|||
} |
|||
} |
|||
|
|||
function toggleAll(id, checked){ |
|||
var placeholder = document.getElementById(id); |
|||
var cases = $(placeholder).find(':checkbox'); |
|||
cases.prop('checked', checked); |
|||
$(cases).parent().children().children().toggleClass("legend-disabled", !checked); |
|||
var choiceContainer; |
|||
|
|||
} |
|||
@ -0,0 +1,462 @@ |
|||
/* |
|||
Licensed to the Apache Software Foundation (ASF) under one or more |
|||
contributor license agreements. See the NOTICE file distributed with |
|||
this work for additional information regarding copyright ownership. |
|||
The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
(the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
http://www.apache.org/licenses/LICENSE-2.0
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
*/ |
|||
var DAY_MS = 86400000; |
|||
var HOUR_MS = 3600000; |
|||
var MINUTE_MS = 60000; |
|||
|
|||
/** |
|||
* From https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math/round
|
|||
* Licensed under https://creativecommons.org/licenses/by-sa/2.5/
|
|||
*/ |
|||
// Closure
|
|||
(function() { |
|||
/** |
|||
* Decimal adjustment of a number. |
|||
* |
|||
* @param {String} type The type of adjustment. |
|||
* @param {Number} value The number. |
|||
* @param {Integer} exp The exponent (the 10 logarithm of the adjustment base). |
|||
* @returns {Number} The adjusted value. |
|||
*/ |
|||
function decimalAdjust(type, value, exp) { |
|||
// If the exp is undefined or zero...
|
|||
if (typeof exp === 'undefined' || +exp === 0) { |
|||
return Math[type](value); |
|||
} |
|||
value = +value; |
|||
exp = +exp; |
|||
// If the value is not a number or the exp is not an integer...
|
|||
if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) { |
|||
return NaN; |
|||
} |
|||
// Shift
|
|||
value = value.toString().split('e'); |
|||
value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp))); |
|||
// Shift back
|
|||
value = value.toString().split('e'); |
|||
return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)); |
|||
} |
|||
|
|||
// Decimal round
|
|||
if (!Math.round10) { |
|||
Math.round10 = function(value, exp) { |
|||
return decimalAdjust('round', value, exp); |
|||
}; |
|||
} |
|||
// Decimal floor
|
|||
if (!Math.floor10) { |
|||
Math.floor10 = function(value, exp) { |
|||
return decimalAdjust('floor', value, exp); |
|||
}; |
|||
} |
|||
// Decimal ceil
|
|||
if (!Math.ceil10) { |
|||
Math.ceil10 = function(value, exp) { |
|||
return decimalAdjust('ceil', value, exp); |
|||
}; |
|||
} |
|||
})(); |
|||
|
|||
/* |
|||
* Suffixes the specified value with a unit |
|||
* The spaced argument defines whether a space character is introduced. |
|||
*/ |
|||
function formatUnit(value, unit, spaced){ |
|||
return spaced ? value + " " + unit : value + unit; |
|||
} |
|||
|
|||
/* |
|||
* Gets a string representing the specified duration in milliseconds. |
|||
* |
|||
* E.g : duration = 20000100, returns "45 min 20 sec 100 ms" |
|||
*/ |
|||
function formatDuration(duration, spaced) { |
|||
var type = $.type(duration); |
|||
if (type === "string") |
|||
return duration; |
|||
|
|||
// Calculate each part of the string
|
|||
var days = Math.floor(duration / 86400000); // 1000 * 60 * 60 * 24 = 1 day
|
|||
duration %= 8640000; |
|||
|
|||
var hours = Math.floor(duration / 3600000); // 1000 * 60 *60 = 1 hour
|
|||
duration %= 3600000; |
|||
|
|||
var minutes = Math.floor(duration / 60000); // 1000 * 60 = 1 minute
|
|||
duration %= 60000; |
|||
|
|||
var seconds = Math.floor(duration / 1000); // 1 second
|
|||
duration %= 1000; |
|||
|
|||
// Add non zero part.
|
|||
var formatArray = []; |
|||
if (days > 0) |
|||
formatArray.push(formatUnit(days, " day(s)", spaced)); |
|||
|
|||
if (hours > 0) |
|||
formatArray.push(formatUnit(hours, " hour(s)", spaced)); |
|||
|
|||
if (minutes > 0) |
|||
formatArray.push(formatUnit(minutes," min", spaced)); |
|||
|
|||
if (seconds > 0) |
|||
formatArray.push(formatUnit(seconds, " sec", spaced)); |
|||
|
|||
if (duration > 0) |
|||
formatArray.push(formatUnit(duration, " ms", spaced)); |
|||
|
|||
// Build the string
|
|||
return formatArray.join(" "); |
|||
} |
|||
|
|||
/* |
|||
* Gets axis label for the specified granularity |
|||
*/ |
|||
function getElapsedTimeLabel(granularity) { |
|||
return "Elapsed Time (granularity: " + formatDuration(granularity) + ")"; |
|||
} |
|||
|
|||
/* |
|||
* Gets time format based on granularity |
|||
*/ |
|||
function getTimeFormat(granularity) { |
|||
if (granularity >= DAY_MS) { |
|||
return "%y/%m/%d"; |
|||
} else if (granularity >= HOUR_MS) { |
|||
return "%m/%d %H"; |
|||
} else if (granularity >= MINUTE_MS) { |
|||
return "%d %H:%M"; |
|||
} else { |
|||
return "%H:%M:%S"; |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* Gets axis label for the specified granularity |
|||
*/ |
|||
function getConnectTimeLabel(granularity) { |
|||
return "Connect Time (granularity: " + formatDuration(granularity) + ")"; |
|||
} |
|||
|
|||
//Get the property value of an object using the specified key
|
|||
//Returns the property value if all properties in the key exist; undefined
|
|||
//otherwise.
|
|||
function getProperty(key, obj) { |
|||
return key.split('.').reduce(function(prop, subprop){ |
|||
return prop && prop[subprop]; |
|||
}, obj); |
|||
} |
|||
|
|||
/* |
|||
* Removes quotes from the specified string |
|||
*/ |
|||
function unquote(str, quoteChar) { |
|||
quoteChar = quoteChar || '"'; |
|||
if (str.length > 0 && str[0] === quoteChar && str[str.length - 1] === quoteChar) |
|||
return str.slice(1, str.length - 1); |
|||
else |
|||
return str; |
|||
}; |
|||
|
|||
/* |
|||
* This comparison function evaluates abscissas to sort array of coordinates. |
|||
*/ |
|||
function compareByXCoordinate(coord1, coord2) { |
|||
return coord2[0] - coord1[0]; |
|||
} |
|||
|
|||
|
|||
/* |
|||
* Followings functions and variables are used to generate statics graphs |
|||
* AND dynamics graphs (if you have the plugin) |
|||
*/ |
|||
|
|||
var showControllersOnly = false; |
|||
var seriesFilter = ""; |
|||
var filtersOnlySampleSeries = true; |
|||
|
|||
// Fixes time stamps
|
|||
function fixTimeStamps(series, offset){ |
|||
$.each(series, function(index, item) { |
|||
$.each(item.data, function(index, coord) { |
|||
coord[0] += offset; |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
// Check if the specified jquery object is a graph
|
|||
function isGraph(object){ |
|||
return object.data('plot') !== undefined; |
|||
} |
|||
|
|||
// Collapse
|
|||
$(function() { |
|||
$('.collapse').on('shown.bs.collapse', function(){ |
|||
collapse(this, false); |
|||
}).on('hidden.bs.collapse', function(){ |
|||
collapse(this, true); |
|||
}); |
|||
}); |
|||
|
|||
$(function() { |
|||
$(".glyphicon").mousedown( function(event){ |
|||
var tmp = $('.in:not(ul)'); |
|||
tmp.parent().parent().parent().find(".fa-chevron-up").removeClass("fa-chevron-down").addClass("fa-chevron-down"); |
|||
tmp.removeClass("in"); |
|||
tmp.addClass("out"); |
|||
}); |
|||
}); |
|||
|
|||
/** |
|||
* Export graph to a PNG |
|||
*/ |
|||
function exportToPNG(graphName, target) { |
|||
var plot = $("#"+graphName).data('plot'); |
|||
var flotCanvas = plot.getCanvas(); |
|||
var image = flotCanvas.toDataURL(); |
|||
image = image.replace("image/png", "image/octet-stream"); |
|||
|
|||
var downloadAttrSupported = ("download" in document.createElement("a")); |
|||
if(downloadAttrSupported === true) { |
|||
target.download = graphName + ".png"; |
|||
target.href = image; |
|||
} |
|||
else { |
|||
document.location.href = image; |
|||
} |
|||
} |
|||
|
|||
// Override the specified graph options to fit the requirements of an overview
|
|||
function prepareOverviewOptions(graphOptions){ |
|||
var overviewOptions = { |
|||
series: { |
|||
shadowSize: 0, |
|||
lines: { |
|||
lineWidth: 1 |
|||
}, |
|||
points: { |
|||
// Show points on overview only when linked graph does not show
|
|||
// lines
|
|||
show: getProperty('series.lines.show', graphOptions) == false, |
|||
radius : 1 |
|||
} |
|||
}, |
|||
xaxis: { |
|||
ticks: 2, |
|||
axisLabel: null |
|||
}, |
|||
yaxis: { |
|||
ticks: 2, |
|||
axisLabel: null |
|||
}, |
|||
legend: { |
|||
show: false, |
|||
container: null |
|||
}, |
|||
grid: { |
|||
hoverable: false |
|||
}, |
|||
tooltip: false |
|||
}; |
|||
return $.extend(true, {}, graphOptions, overviewOptions); |
|||
} |
|||
|
|||
function prepareOptions(options, data) { |
|||
options.canvas = true; |
|||
var extraOptions = data.extraOptions; |
|||
if(extraOptions !== undefined){ |
|||
var xOffset = options.xaxis.mode === "time" ? 28800000 : 0; |
|||
var yOffset = options.yaxis.mode === "time" ? 28800000 : 0; |
|||
|
|||
if(!isNaN(extraOptions.minX)) |
|||
options.xaxis.min = parseFloat(extraOptions.minX) + xOffset; |
|||
|
|||
if(!isNaN(extraOptions.maxX)) |
|||
options.xaxis.max = parseFloat(extraOptions.maxX) + xOffset; |
|||
|
|||
if(!isNaN(extraOptions.minY)) |
|||
options.yaxis.min = parseFloat(extraOptions.minY) + yOffset; |
|||
|
|||
if(!isNaN(extraOptions.maxY)) |
|||
options.yaxis.max = parseFloat(extraOptions.maxY) + yOffset; |
|||
} |
|||
} |
|||
|
|||
// Filter, mark series and sort data
|
|||
/** |
|||
* @param data |
|||
* @param noMatchColor if defined and true, series.color are not matched with index |
|||
* @param ignoreFilterParam If true we don't apply seriesFilter |
|||
*/ |
|||
function prepareSeries(data, noMatchColor, ignoreFilterParam){ |
|||
var result = data.result; |
|||
var ignoreFilter = ignoreFilterParam === true; |
|||
// Keep only series when needed
|
|||
if(!ignoreFilter && seriesFilter && (!filtersOnlySampleSeries || result.supportsControllersDiscrimination)){ |
|||
// Insensitive case matching
|
|||
var regexp = new RegExp(seriesFilter, 'i'); |
|||
result.series = $.grep(result.series, function(series, index){ |
|||
return regexp.test(series.label); |
|||
}); |
|||
} |
|||
|
|||
// Keep only controllers series when supported and needed
|
|||
if(result.supportsControllersDiscrimination && showControllersOnly){ |
|||
result.series = $.grep(result.series, function(series, index){ |
|||
return series.isController; |
|||
}); |
|||
} |
|||
|
|||
// Sort data and mark series
|
|||
$.each(result.series, function(index, series) { |
|||
series.data.sort(compareByXCoordinate); |
|||
if(!(noMatchColor && noMatchColor===true)) { |
|||
series.color = index; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
// Set the zoom on the specified plot object
|
|||
function zoomPlot(plot, xmin, xmax, ymin, ymax){ |
|||
var axes = plot.getAxes(); |
|||
// Override axes min and max options
|
|||
$.extend(true, axes, { |
|||
xaxis: { |
|||
options : { min: xmin, max: xmax } |
|||
}, |
|||
yaxis: { |
|||
options : { min: ymin, max: ymax } |
|||
} |
|||
}); |
|||
|
|||
// Redraw the plot
|
|||
plot.setupGrid(); |
|||
plot.draw(); |
|||
} |
|||
|
|||
// Prepares DOM items to add zoom function on the specified graph
|
|||
function setGraphZoomable(graphSelector, overviewSelector){ |
|||
var graph = $(graphSelector); |
|||
var overview = $(overviewSelector); |
|||
|
|||
// Ignore mouse down event
|
|||
graph.bind("mousedown", function() { return false; }); |
|||
overview.bind("mousedown", function() { return false; }); |
|||
|
|||
// Zoom on selection
|
|||
graph.bind("plotselected", function (event, ranges) { |
|||
// clamp the zooming to prevent infinite zoom
|
|||
if (ranges.xaxis.to - ranges.xaxis.from < 0.00001) { |
|||
ranges.xaxis.to = ranges.xaxis.from + 0.00001; |
|||
} |
|||
if (ranges.yaxis.to - ranges.yaxis.from < 0.00001) { |
|||
ranges.yaxis.to = ranges.yaxis.from + 0.00001; |
|||
} |
|||
|
|||
// Do the zooming
|
|||
var plot = graph.data('plot'); |
|||
zoomPlot(plot, ranges.xaxis.from, ranges.xaxis.to, ranges.yaxis.from, ranges.yaxis.to); |
|||
plot.clearSelection(); |
|||
|
|||
// Synchronize overview selection
|
|||
overview.data('plot').setSelection(ranges, true); |
|||
}); |
|||
|
|||
// Zoom linked graph on overview selection
|
|||
overview.bind("plotselected", function (event, ranges) { |
|||
graph.data('plot').setSelection(ranges); |
|||
}); |
|||
|
|||
// Reset linked graph zoom when reseting overview selection
|
|||
overview.bind("plotunselected", function () { |
|||
var overviewAxes = overview.data('plot').getAxes(); |
|||
zoomPlot(graph.data('plot'), overviewAxes.xaxis.min, overviewAxes.xaxis.max, overviewAxes.yaxis.min, overviewAxes.yaxis.max); |
|||
}); |
|||
} |
|||
|
|||
// Prepares data to be consumed by plot plugins
|
|||
function prepareData(series, choiceContainer, customizeSeries){ |
|||
var datasets = []; |
|||
|
|||
// Add only selected series to the data set
|
|||
choiceContainer.find("input:checked").each(function (index, item) { |
|||
var key = $(item).attr("name"); |
|||
var i = 0; |
|||
var size = series.length; |
|||
while(i < size && series[i].label != key) |
|||
i++; |
|||
if(i < size){ |
|||
var currentSeries = series[i]; |
|||
datasets.push(currentSeries); |
|||
if(customizeSeries) |
|||
customizeSeries(currentSeries); |
|||
} |
|||
}); |
|||
return datasets; |
|||
} |
|||
|
|||
/* |
|||
* Ignore case comparator |
|||
*/ |
|||
function sortAlphaCaseless(a,b){ |
|||
return a.toLowerCase() > b.toLowerCase() ? 1 : -1; |
|||
}; |
|||
|
|||
function createLegend(choiceContainer, infos) { |
|||
// Sort series by name
|
|||
var keys = []; |
|||
$.each(infos.data.result.series, function(index, series){ |
|||
keys.push(series.label); |
|||
}); |
|||
keys.sort(sortAlphaCaseless); |
|||
|
|||
// Create list of series with support of activation/deactivation
|
|||
$.each(keys, function(index, key) { |
|||
var id = choiceContainer.attr('id') + index; |
|||
$('<li />') |
|||
.append($('<input id="' + id + '" name="' + key + '" type="checkbox" checked="checked" hidden />')) |
|||
.append($('<label />', { 'text': key , 'for': id })) |
|||
.appendTo(choiceContainer); |
|||
}); |
|||
choiceContainer.find("label").click( function(){ |
|||
if (this.style.color !== "rgb(129, 129, 129)" ){ |
|||
this.style.color="#818181"; |
|||
}else { |
|||
this.style.color="black"; |
|||
} |
|||
$(this).parent().children().children().toggleClass("legend-disabled"); |
|||
}); |
|||
choiceContainer.find("label").mousedown( function(event){ |
|||
event.preventDefault(); |
|||
}); |
|||
choiceContainer.find("label").mouseenter(function(){ |
|||
this.style.cursor="pointer"; |
|||
}); |
|||
|
|||
// Recreate graphe on series activation toggle
|
|||
choiceContainer.find("input").click(function(){ |
|||
infos.createGraph(); |
|||
}); |
|||
} |
|||
|
|||
// Unchecks all boxes for "Hide all samples" functionality
|
|||
function uncheckAll(id){ |
|||
toggleAll(id, false); |
|||
} |
|||
|
|||
// Checks all boxes for "Show all samples" functionality
|
|||
function checkAll(id){ |
|||
toggleAll(id, true); |
|||
} |
|||
@ -0,0 +1,235 @@ |
|||
/* |
|||
Licensed to the Apache Software Foundation (ASF) under one or more |
|||
contributor license agreements. See the NOTICE file distributed with |
|||
this work for additional information regarding copyright ownership. |
|||
The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
(the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0
|
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
*/ |
|||
var showControllersOnly = false; |
|||
var seriesFilter = ""; |
|||
var filtersOnlySampleSeries = true; |
|||
|
|||
/* |
|||
* Add header in statistics table to group metrics by category |
|||
* format |
|||
* |
|||
*/ |
|||
function summaryTableHeader(header) { |
|||
var newRow = header.insertRow(-1); |
|||
newRow.className = "tablesorter-no-sort"; |
|||
var cell = document.createElement('th'); |
|||
cell.setAttribute("data-sorter", false); |
|||
cell.colSpan = 1; |
|||
cell.innerHTML = "Requests"; |
|||
newRow.appendChild(cell); |
|||
|
|||
cell = document.createElement('th'); |
|||
cell.setAttribute("data-sorter", false); |
|||
cell.colSpan = 3; |
|||
cell.innerHTML = "Executions"; |
|||
newRow.appendChild(cell); |
|||
|
|||
cell = document.createElement('th'); |
|||
cell.setAttribute("data-sorter", false); |
|||
cell.colSpan = 7; |
|||
cell.innerHTML = "Response Times (ms)"; |
|||
newRow.appendChild(cell); |
|||
|
|||
cell = document.createElement('th'); |
|||
cell.setAttribute("data-sorter", false); |
|||
cell.colSpan = 1; |
|||
cell.innerHTML = "Throughput"; |
|||
newRow.appendChild(cell); |
|||
|
|||
cell = document.createElement('th'); |
|||
cell.setAttribute("data-sorter", false); |
|||
cell.colSpan = 2; |
|||
cell.innerHTML = "Network (KB/sec)"; |
|||
newRow.appendChild(cell); |
|||
} |
|||
|
|||
/* |
|||
* Populates the table identified by id parameter with the specified data and |
|||
* format |
|||
* |
|||
*/ |
|||
function createTable(table, info, formatter, defaultSorts, seriesIndex, headerCreator) { |
|||
var tableRef = table[0]; |
|||
|
|||
// Create header and populate it with data.titles array
|
|||
var header = tableRef.createTHead(); |
|||
|
|||
// Call callback is available
|
|||
if(headerCreator) { |
|||
headerCreator(header); |
|||
} |
|||
|
|||
var newRow = header.insertRow(-1); |
|||
for (var index = 0; index < info.titles.length; index++) { |
|||
var cell = document.createElement('th'); |
|||
cell.innerHTML = info.titles[index]; |
|||
newRow.appendChild(cell); |
|||
} |
|||
|
|||
var tBody; |
|||
|
|||
// Create overall body if defined
|
|||
if(info.overall){ |
|||
tBody = document.createElement('tbody'); |
|||
tBody.className = "tablesorter-no-sort"; |
|||
tableRef.appendChild(tBody); |
|||
var newRow = tBody.insertRow(-1); |
|||
var data = info.overall.data; |
|||
for(var index=0;index < data.length; index++){ |
|||
var cell = newRow.insertCell(-1); |
|||
cell.innerHTML = formatter ? formatter(index, data[index]): data[index]; |
|||
} |
|||
} |
|||
|
|||
// Create regular body
|
|||
tBody = document.createElement('tbody'); |
|||
tableRef.appendChild(tBody); |
|||
|
|||
var regexp; |
|||
if(seriesFilter) { |
|||
regexp = new RegExp(seriesFilter, 'i'); |
|||
} |
|||
// Populate body with data.items array
|
|||
for(var index=0; index < info.items.length; index++){ |
|||
var item = info.items[index]; |
|||
if((!regexp || filtersOnlySampleSeries && !info.supportsControllersDiscrimination || regexp.test(item.data[seriesIndex])) |
|||
&& |
|||
(!showControllersOnly || !info.supportsControllersDiscrimination || item.isController)){ |
|||
if(item.data.length > 0) { |
|||
var newRow = tBody.insertRow(-1); |
|||
for(var col=0; col < item.data.length; col++){ |
|||
var cell = newRow.insertCell(-1); |
|||
cell.innerHTML = formatter ? formatter(col, item.data[col]) : item.data[col]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Add support of columns sort
|
|||
table.tablesorter({sortList : defaultSorts}); |
|||
} |
|||
|
|||
$(document).ready(function() { |
|||
|
|||
// Customize table sorter default options
|
|||
$.extend( $.tablesorter.defaults, { |
|||
theme: 'blue', |
|||
cssInfoBlock: "tablesorter-no-sort", |
|||
widthFixed: true, |
|||
widgets: ['zebra'] |
|||
}); |
|||
|
|||
var data = {"OkPercent": 100.0, "KoPercent": 0.0}; |
|||
var dataset = [ |
|||
{ |
|||
"label" : "FAIL", |
|||
"data" : data.KoPercent, |
|||
"color" : "#FF6347" |
|||
}, |
|||
{ |
|||
"label" : "PASS", |
|||
"data" : data.OkPercent, |
|||
"color" : "#9ACD32" |
|||
}]; |
|||
$.plot($("#flot-requests-summary"), dataset, { |
|||
series : { |
|||
pie : { |
|||
show : true, |
|||
radius : 1, |
|||
label : { |
|||
show : true, |
|||
radius : 3 / 4, |
|||
formatter : function(label, series) { |
|||
return '<div style="font-size:8pt;text-align:center;padding:2px;color:white;">' |
|||
+ label |
|||
+ '<br/>' |
|||
+ Math.round10(series.percent, -2) |
|||
+ '%</div>'; |
|||
}, |
|||
background : { |
|||
opacity : 0.5, |
|||
color : '#000' |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
legend : { |
|||
show : true |
|||
} |
|||
}); |
|||
|
|||
// Creates APDEX table
|
|||
createTable($("#apdexTable"), {"supportsControllersDiscrimination": true, "overall": {"data": [0.999465625, 500, 1500, "Total"], "isController": false}, "titles": ["Apdex", "T (Toleration threshold)", "F (Frustration threshold)", "Label"], "items": [{"data": [0.99893125, 500, 1500, "Create a book"], "isController": false}, {"data": [1.0, 500, 1500, "Get book by id"], "isController": false}]}, function(index, item){ |
|||
switch(index){ |
|||
case 0: |
|||
item = item.toFixed(3); |
|||
break; |
|||
case 1: |
|||
case 2: |
|||
item = formatDuration(item); |
|||
break; |
|||
} |
|||
return item; |
|||
}, [[0, 0]], 3); |
|||
|
|||
// Create statistics table
|
|||
createTable($("#statisticsTable"), {"supportsControllersDiscrimination": true, "overall": {"data": ["Total", 160000, 0, 0.0, 47.38238750000128, 0, 1628, 1.0, 6.0, 8.0, 10.0, 8074.690890739339, 1761.9741889824627, 1742.2605881749935], "isController": false}, "titles": ["Label", "#Samples", "FAIL", "Error %", "Average", "Min", "Max", "Median", "90th pct", "95th pct", "99th pct", "Transactions/s", "Received", "Sent"], "items": [{"data": ["Create a book", 80000, 0, 0.0, 51.883337500000096, 1, 1628, 8.0, 21.0, 24.0, 28.0, 4037.549207630968, 776.7550721711921, 965.5934467895175], "isController": false}, {"data": ["Get book by id", 80000, 0, 0.0, 42.8814375, 0, 311, 4.0, 15.0, 18.0, 22.0, 4440.74382459062, 1083.7021123889813, 854.3227865667499], "isController": false}]}, function(index, item){ |
|||
switch(index){ |
|||
// Errors pct
|
|||
case 3: |
|||
item = item.toFixed(2) + '%'; |
|||
break; |
|||
// Mean
|
|||
case 4: |
|||
// Mean
|
|||
case 7: |
|||
// Median
|
|||
case 8: |
|||
// Percentile 1
|
|||
case 9: |
|||
// Percentile 2
|
|||
case 10: |
|||
// Percentile 3
|
|||
case 11: |
|||
// Throughput
|
|||
case 12: |
|||
// Kbytes/s
|
|||
case 13: |
|||
// Sent Kbytes/s
|
|||
item = item.toFixed(2); |
|||
break; |
|||
} |
|||
return item; |
|||
}, [[0, 0]], 0, summaryTableHeader); |
|||
|
|||
// Create error table
|
|||
createTable($("#errorsTable"), {"supportsControllersDiscrimination": false, "titles": ["Type of error", "Number of errors", "% in errors", "% in all samples"], "items": []}, function(index, item){ |
|||
switch(index){ |
|||
case 2: |
|||
case 3: |
|||
item = item.toFixed(2) + '%'; |
|||
break; |
|||
} |
|||
return item; |
|||
}, [[1, 1]]); |
|||
|
|||
// Create top5 errors by sampler
|
|||
createTable($("#top5ErrorsBySamplerTable"), {"supportsControllersDiscrimination": false, "overall": {"data": ["Total", 160000, 0, "", "", "", "", "", "", "", "", "", ""], "isController": false}, "titles": ["Sample", "#Samples", "#Errors", "Error", "#Errors", "Error", "#Errors", "Error", "#Errors", "Error", "#Errors", "Error", "#Errors"], "items": [{"data": [], "isController": false}, {"data": [], "isController": false}]}, function(index, item){ |
|||
return item; |
|||
}, [[0, 0]], 0); |
|||
|
|||
}); |
|||
File diff suppressed because one or more lines are too long
@ -0,0 +1,402 @@ |
|||
/** |
|||
* @license jahashtable, a JavaScript implementation of a hash table. It creates a single constructor function called |
|||
* Hashtable in the global scope. |
|||
* |
|||
* http://www.timdown.co.uk/jshashtable/
|
|||
* Copyright %%build:year%% Tim Down. |
|||
* Version: %%build:version%% |
|||
* Build date: %%build:date%% |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
var Hashtable = (function(UNDEFINED) { |
|||
var FUNCTION = "function", STRING = "string", UNDEF = "undefined"; |
|||
|
|||
// Require Array.prototype.splice, Object.prototype.hasOwnProperty and encodeURIComponent. In environments not
|
|||
// having these (e.g. IE <= 5), we bail out now and leave Hashtable null.
|
|||
if (typeof encodeURIComponent == UNDEF || |
|||
Array.prototype.splice === UNDEFINED || |
|||
Object.prototype.hasOwnProperty === UNDEFINED) { |
|||
return null; |
|||
} |
|||
|
|||
function toStr(obj) { |
|||
return (typeof obj == STRING) ? obj : "" + obj; |
|||
} |
|||
|
|||
function hashObject(obj) { |
|||
var hashCode; |
|||
if (typeof obj == STRING) { |
|||
return obj; |
|||
} else if (typeof obj.hashCode == FUNCTION) { |
|||
// Check the hashCode method really has returned a string
|
|||
hashCode = obj.hashCode(); |
|||
return (typeof hashCode == STRING) ? hashCode : hashObject(hashCode); |
|||
} else { |
|||
return toStr(obj); |
|||
} |
|||
} |
|||
|
|||
function merge(o1, o2) { |
|||
for (var i in o2) { |
|||
if (o2.hasOwnProperty(i)) { |
|||
o1[i] = o2[i]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
function equals_fixedValueHasEquals(fixedValue, variableValue) { |
|||
return fixedValue.equals(variableValue); |
|||
} |
|||
|
|||
function equals_fixedValueNoEquals(fixedValue, variableValue) { |
|||
return (typeof variableValue.equals == FUNCTION) ? |
|||
variableValue.equals(fixedValue) : (fixedValue === variableValue); |
|||
} |
|||
|
|||
function createKeyValCheck(kvStr) { |
|||
return function(kv) { |
|||
if (kv === null) { |
|||
throw new Error("null is not a valid " + kvStr); |
|||
} else if (kv === UNDEFINED) { |
|||
throw new Error(kvStr + " must not be undefined"); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
var checkKey = createKeyValCheck("key"), checkValue = createKeyValCheck("value"); |
|||
|
|||
/*----------------------------------------------------------------------------------------------------------------*/ |
|||
|
|||
function Bucket(hash, firstKey, firstValue, equalityFunction) { |
|||
this[0] = hash; |
|||
this.entries = []; |
|||
this.addEntry(firstKey, firstValue); |
|||
|
|||
if (equalityFunction !== null) { |
|||
this.getEqualityFunction = function() { |
|||
return equalityFunction; |
|||
}; |
|||
} |
|||
} |
|||
|
|||
var EXISTENCE = 0, ENTRY = 1, ENTRY_INDEX_AND_VALUE = 2; |
|||
|
|||
function createBucketSearcher(mode) { |
|||
return function(key) { |
|||
var i = this.entries.length, entry, equals = this.getEqualityFunction(key); |
|||
while (i--) { |
|||
entry = this.entries[i]; |
|||
if ( equals(key, entry[0]) ) { |
|||
switch (mode) { |
|||
case EXISTENCE: |
|||
return true; |
|||
case ENTRY: |
|||
return entry; |
|||
case ENTRY_INDEX_AND_VALUE: |
|||
return [ i, entry[1] ]; |
|||
} |
|||
} |
|||
} |
|||
return false; |
|||
}; |
|||
} |
|||
|
|||
function createBucketLister(entryProperty) { |
|||
return function(aggregatedArr) { |
|||
var startIndex = aggregatedArr.length; |
|||
for (var i = 0, entries = this.entries, len = entries.length; i < len; ++i) { |
|||
aggregatedArr[startIndex + i] = entries[i][entryProperty]; |
|||
} |
|||
}; |
|||
} |
|||
|
|||
Bucket.prototype = { |
|||
getEqualityFunction: function(searchValue) { |
|||
return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals; |
|||
}, |
|||
|
|||
getEntryForKey: createBucketSearcher(ENTRY), |
|||
|
|||
getEntryAndIndexForKey: createBucketSearcher(ENTRY_INDEX_AND_VALUE), |
|||
|
|||
removeEntryForKey: function(key) { |
|||
var result = this.getEntryAndIndexForKey(key); |
|||
if (result) { |
|||
this.entries.splice(result[0], 1); |
|||
return result[1]; |
|||
} |
|||
return null; |
|||
}, |
|||
|
|||
addEntry: function(key, value) { |
|||
this.entries.push( [key, value] ); |
|||
}, |
|||
|
|||
keys: createBucketLister(0), |
|||
|
|||
values: createBucketLister(1), |
|||
|
|||
getEntries: function(destEntries) { |
|||
var startIndex = destEntries.length; |
|||
for (var i = 0, entries = this.entries, len = entries.length; i < len; ++i) { |
|||
// Clone the entry stored in the bucket before adding to array
|
|||
destEntries[startIndex + i] = entries[i].slice(0); |
|||
} |
|||
}, |
|||
|
|||
containsKey: createBucketSearcher(EXISTENCE), |
|||
|
|||
containsValue: function(value) { |
|||
var entries = this.entries, i = entries.length; |
|||
while (i--) { |
|||
if ( value === entries[i][1] ) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
}; |
|||
|
|||
/*----------------------------------------------------------------------------------------------------------------*/ |
|||
|
|||
// Supporting functions for searching hashtable buckets
|
|||
|
|||
function searchBuckets(buckets, hash) { |
|||
var i = buckets.length, bucket; |
|||
while (i--) { |
|||
bucket = buckets[i]; |
|||
if (hash === bucket[0]) { |
|||
return i; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
function getBucketForHash(bucketsByHash, hash) { |
|||
var bucket = bucketsByHash[hash]; |
|||
|
|||
// Check that this is a genuine bucket and not something inherited from the bucketsByHash's prototype
|
|||
return ( bucket && (bucket instanceof Bucket) ) ? bucket : null; |
|||
} |
|||
|
|||
/*----------------------------------------------------------------------------------------------------------------*/ |
|||
|
|||
function Hashtable() { |
|||
var buckets = []; |
|||
var bucketsByHash = {}; |
|||
var properties = { |
|||
replaceDuplicateKey: true, |
|||
hashCode: hashObject, |
|||
equals: null |
|||
}; |
|||
|
|||
var arg0 = arguments[0], arg1 = arguments[1]; |
|||
if (arg1 !== UNDEFINED) { |
|||
properties.hashCode = arg0; |
|||
properties.equals = arg1; |
|||
} else if (arg0 !== UNDEFINED) { |
|||
merge(properties, arg0); |
|||
} |
|||
|
|||
var hashCode = properties.hashCode, equals = properties.equals; |
|||
|
|||
this.properties = properties; |
|||
|
|||
this.put = function(key, value) { |
|||
checkKey(key); |
|||
checkValue(value); |
|||
var hash = hashCode(key), bucket, bucketEntry, oldValue = null; |
|||
|
|||
// Check if a bucket exists for the bucket key
|
|||
bucket = getBucketForHash(bucketsByHash, hash); |
|||
if (bucket) { |
|||
// Check this bucket to see if it already contains this key
|
|||
bucketEntry = bucket.getEntryForKey(key); |
|||
if (bucketEntry) { |
|||
// This bucket entry is the current mapping of key to value, so replace the old value.
|
|||
// Also, we optionally replace the key so that the latest key is stored.
|
|||
if (properties.replaceDuplicateKey) { |
|||
bucketEntry[0] = key; |
|||
} |
|||
oldValue = bucketEntry[1]; |
|||
bucketEntry[1] = value; |
|||
} else { |
|||
// The bucket does not contain an entry for this key, so add one
|
|||
bucket.addEntry(key, value); |
|||
} |
|||
} else { |
|||
// No bucket exists for the key, so create one and put our key/value mapping in
|
|||
bucket = new Bucket(hash, key, value, equals); |
|||
buckets.push(bucket); |
|||
bucketsByHash[hash] = bucket; |
|||
} |
|||
return oldValue; |
|||
}; |
|||
|
|||
this.get = function(key) { |
|||
checkKey(key); |
|||
|
|||
var hash = hashCode(key); |
|||
|
|||
// Check if a bucket exists for the bucket key
|
|||
var bucket = getBucketForHash(bucketsByHash, hash); |
|||
if (bucket) { |
|||
// Check this bucket to see if it contains this key
|
|||
var bucketEntry = bucket.getEntryForKey(key); |
|||
if (bucketEntry) { |
|||
// This bucket entry is the current mapping of key to value, so return the value.
|
|||
return bucketEntry[1]; |
|||
} |
|||
} |
|||
return null; |
|||
}; |
|||
|
|||
this.containsKey = function(key) { |
|||
checkKey(key); |
|||
var bucketKey = hashCode(key); |
|||
|
|||
// Check if a bucket exists for the bucket key
|
|||
var bucket = getBucketForHash(bucketsByHash, bucketKey); |
|||
|
|||
return bucket ? bucket.containsKey(key) : false; |
|||
}; |
|||
|
|||
this.containsValue = function(value) { |
|||
checkValue(value); |
|||
var i = buckets.length; |
|||
while (i--) { |
|||
if (buckets[i].containsValue(value)) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
}; |
|||
|
|||
this.clear = function() { |
|||
buckets.length = 0; |
|||
bucketsByHash = {}; |
|||
}; |
|||
|
|||
this.isEmpty = function() { |
|||
return !buckets.length; |
|||
}; |
|||
|
|||
var createBucketAggregator = function(bucketFuncName) { |
|||
return function() { |
|||
var aggregated = [], i = buckets.length; |
|||
while (i--) { |
|||
buckets[i][bucketFuncName](aggregated); |
|||
} |
|||
return aggregated; |
|||
}; |
|||
}; |
|||
|
|||
this.keys = createBucketAggregator("keys"); |
|||
this.values = createBucketAggregator("values"); |
|||
this.entries = createBucketAggregator("getEntries"); |
|||
|
|||
this.remove = function(key) { |
|||
checkKey(key); |
|||
|
|||
var hash = hashCode(key), bucketIndex, oldValue = null; |
|||
|
|||
// Check if a bucket exists for the bucket key
|
|||
var bucket = getBucketForHash(bucketsByHash, hash); |
|||
|
|||
if (bucket) { |
|||
// Remove entry from this bucket for this key
|
|||
oldValue = bucket.removeEntryForKey(key); |
|||
if (oldValue !== null) { |
|||
// Entry was removed, so check if bucket is empty
|
|||
if (bucket.entries.length == 0) { |
|||
// Bucket is empty, so remove it from the bucket collections
|
|||
bucketIndex = searchBuckets(buckets, hash); |
|||
buckets.splice(bucketIndex, 1); |
|||
delete bucketsByHash[hash]; |
|||
} |
|||
} |
|||
} |
|||
return oldValue; |
|||
}; |
|||
|
|||
this.size = function() { |
|||
var total = 0, i = buckets.length; |
|||
while (i--) { |
|||
total += buckets[i].entries.length; |
|||
} |
|||
return total; |
|||
}; |
|||
} |
|||
|
|||
Hashtable.prototype = { |
|||
each: function(callback) { |
|||
var entries = this.entries(), i = entries.length, entry; |
|||
while (i--) { |
|||
entry = entries[i]; |
|||
callback(entry[0], entry[1]); |
|||
} |
|||
}, |
|||
|
|||
equals: function(hashtable) { |
|||
var keys, key, val, count = this.size(); |
|||
if (count == hashtable.size()) { |
|||
keys = this.keys(); |
|||
while (count--) { |
|||
key = keys[count]; |
|||
val = hashtable.get(key); |
|||
if (val === null || val !== this.get(key)) { |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
return false; |
|||
}, |
|||
|
|||
putAll: function(hashtable, conflictCallback) { |
|||
var entries = hashtable.entries(); |
|||
var entry, key, value, thisValue, i = entries.length; |
|||
var hasConflictCallback = (typeof conflictCallback == FUNCTION); |
|||
while (i--) { |
|||
entry = entries[i]; |
|||
key = entry[0]; |
|||
value = entry[1]; |
|||
|
|||
// Check for a conflict. The default behaviour is to overwrite the value for an existing key
|
|||
if ( hasConflictCallback && (thisValue = this.get(key)) ) { |
|||
value = conflictCallback(key, thisValue, value); |
|||
} |
|||
this.put(key, value); |
|||
} |
|||
}, |
|||
|
|||
clone: function() { |
|||
var clone = new Hashtable(this.properties); |
|||
clone.putAll(this); |
|||
return clone; |
|||
} |
|||
}; |
|||
|
|||
Hashtable.prototype.toQueryString = function() { |
|||
var entries = this.entries(), i = entries.length, entry; |
|||
var parts = []; |
|||
while (i--) { |
|||
entry = entries[i]; |
|||
parts[i] = encodeURIComponent( toStr(entry[0]) ) + "=" + encodeURIComponent( toStr(entry[1]) ); |
|||
} |
|||
return parts.join("&"); |
|||
}; |
|||
|
|||
return Hashtable; |
|||
})(); |
|||
File diff suppressed because one or more lines are too long
@ -0,0 +1,117 @@ |
|||
/*! |
|||
* jQuery Cookie Plugin v1.4.1 |
|||
* https://github.com/carhartl/jquery-cookie
|
|||
* |
|||
* Copyright 2013 Klaus Hartl |
|||
* Released under the MIT license |
|||
*/ |
|||
(function (factory) { |
|||
if (typeof define === 'function' && define.amd) { |
|||
// AMD
|
|||
define(['jquery'], factory); |
|||
} else if (typeof exports === 'object') { |
|||
// CommonJS
|
|||
factory(require('jquery')); |
|||
} else { |
|||
// Browser globals
|
|||
factory(jQuery); |
|||
} |
|||
}(function ($) { |
|||
|
|||
var pluses = /\+/g; |
|||
|
|||
function encode(s) { |
|||
return config.raw ? s : encodeURIComponent(s); |
|||
} |
|||
|
|||
function decode(s) { |
|||
return config.raw ? s : decodeURIComponent(s); |
|||
} |
|||
|
|||
function stringifyCookieValue(value) { |
|||
return encode(config.json ? JSON.stringify(value) : String(value)); |
|||
} |
|||
|
|||
function parseCookieValue(s) { |
|||
if (s.indexOf('"') === 0) { |
|||
// This is a quoted cookie as according to RFC2068, unescape...
|
|||
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); |
|||
} |
|||
|
|||
try { |
|||
// Replace server-side written pluses with spaces.
|
|||
// If we can't decode the cookie, ignore it, it's unusable.
|
|||
// If we can't parse the cookie, ignore it, it's unusable.
|
|||
s = decodeURIComponent(s.replace(pluses, ' ')); |
|||
return config.json ? JSON.parse(s) : s; |
|||
} catch(e) {} |
|||
} |
|||
|
|||
function read(s, converter) { |
|||
var value = config.raw ? s : parseCookieValue(s); |
|||
return $.isFunction(converter) ? converter(value) : value; |
|||
} |
|||
|
|||
var config = $.cookie = function (key, value, options) { |
|||
|
|||
// Write
|
|||
|
|||
if (value !== undefined && !$.isFunction(value)) { |
|||
options = $.extend({}, config.defaults, options); |
|||
|
|||
if (typeof options.expires === 'number') { |
|||
var days = options.expires, t = options.expires = new Date(); |
|||
t.setTime(+t + days * 864e+5); |
|||
} |
|||
|
|||
return (document.cookie = [ |
|||
encode(key), '=', stringifyCookieValue(value), |
|||
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
|
|||
options.path ? '; path=' + options.path : '', |
|||
options.domain ? '; domain=' + options.domain : '', |
|||
options.secure ? '; secure' : '' |
|||
].join('')); |
|||
} |
|||
|
|||
// Read
|
|||
|
|||
var result = key ? undefined : {}; |
|||
|
|||
// To prevent the for loop in the first place assign an empty array
|
|||
// in case there are no cookies at all. Also prevents odd result when
|
|||
// calling $.cookie().
|
|||
var cookies = document.cookie ? document.cookie.split('; ') : []; |
|||
|
|||
for (var i = 0, l = cookies.length; i < l; i++) { |
|||
var parts = cookies[i].split('='); |
|||
var name = decode(parts.shift()); |
|||
var cookie = parts.join('='); |
|||
|
|||
if (key && key === name) { |
|||
// If second argument (value) is a function it's a converter...
|
|||
result = read(cookie, value); |
|||
break; |
|||
} |
|||
|
|||
// Prevent storing a cookie that we couldn't decode.
|
|||
if (!key && (cookie = read(cookie)) !== undefined) { |
|||
result[name] = cookie; |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
}; |
|||
|
|||
config.defaults = {}; |
|||
|
|||
$.removeCookie = function (key, options) { |
|||
if ($.cookie(key) === undefined) { |
|||
return false; |
|||
} |
|||
|
|||
// Must not alter options, thus extending a fresh object...
|
|||
$.cookie(key, '', $.extend({}, options, { expires: -1 })); |
|||
return !$.cookie(key); |
|||
}; |
|||
|
|||
})); |
|||
@ -0,0 +1,83 @@ |
|||
/* Flot plugin for stacking data sets rather than overlyaing them. |
|||
|
|||
Copyright (c) 2007-2014 IOLA and Ole Laursen. |
|||
Licensed under the MIT license. |
|||
|
|||
The plugin assumes the data is sorted on x (or y if stacking horizontally). |
|||
For line charts, it is assumed that if a line has an undefined gap (from a |
|||
null point), then the line above it should have the same gap - insert zeros |
|||
instead of "null" if you want another behaviour. This also holds for the start |
|||
and end of the chart. Note that stacking a mix of positive and negative values |
|||
in most instances doesn't make sense (so it looks weird). |
|||
|
|||
Two or more series are stacked when their "stack" attribute is set to the same |
|||
key (which can be any number or string or just "true"). To specify the default |
|||
stack, you can set the stack option like this: |
|||
|
|||
series: { |
|||
stack: null/false, true, or a key (number/string) |
|||
} |
|||
|
|||
You can also specify it for a single series, like this: |
|||
|
|||
$.plot( $("#placeholder"), [{ |
|||
data: [ ... ], |
|||
stack: true |
|||
}]) |
|||
|
|||
The stacking order is determined by the order of the data series in the array |
|||
(later series end up on top of the previous). |
|||
|
|||
Internally, the plugin modifies the datapoints in each series, adding an |
|||
offset to the y value. For line series, extra data points are inserted through |
|||
interpolation. If there's a second y value, it's also adjusted (e.g for bar |
|||
charts or filled areas). |
|||
|
|||
*/ |
|||
|
|||
/** |
|||
* Patched version as per https://github.com/flot/flot/issues/326
|
|||
*/ |
|||
(function ($) { |
|||
var options = { |
|||
series: { stack: null } // or number/string
|
|||
}; |
|||
|
|||
function init(plot) { |
|||
|
|||
// will be built up dynamically as a hash from x-value to
|
|||
var stackBases = {}; |
|||
|
|||
function stackData(plot, s, datapoints) { |
|||
if (s.stack == null || s.stack === false) |
|||
return; |
|||
|
|||
var newPoints = []; |
|||
|
|||
for (var i=0; i < datapoints.points.length; i += 3) { |
|||
|
|||
if (!stackBases[datapoints.points[i]]) { |
|||
stackBases[datapoints.points[i]] = 0; |
|||
} |
|||
|
|||
// note that the values need to be turned into absolute y-values.
|
|||
// in other words, if you were to stack (x, y1), (x, y2), and (x, y3), (each from different series, which is where stackBases comes in),
|
|||
// you'd want the new points to be (x, y1, 0), (x, y1+y2, y1), (x,y1+y2+y3, y1+y2)
|
|||
// generally, (x, thisValue + (base up to this point), + (base up tothis point))
|
|||
newPoints[i] = datapoints.points[i]; |
|||
newPoints[i+1] = datapoints.points[i+1] + stackBases[datapoints.points[i]]; |
|||
newPoints[i+2] = stackBases[datapoints.points[i]]; |
|||
stackBases[datapoints.points[i]] += datapoints.points[i+1]; |
|||
} |
|||
datapoints.points = newPoints; |
|||
} |
|||
plot.hooks.processDatapoints.push(stackData); |
|||
} |
|||
|
|||
$.plot.plugins.push({ |
|||
init: init, |
|||
options: options, |
|||
name: 'stack', |
|||
version: '1.2-patch-326' |
|||
}); |
|||
})(jQuery); |
|||
@ -0,0 +1 @@ |
|||
(function(k){var a=new Hashtable();var f=["ae","au","ca","cn","eg","gb","hk","il","in","jp","sk","th","tw","us"];var b=["at","br","de","dk","es","gr","it","nl","pt","tr","vn"];var i=["cz","fi","fr","ru","se","pl"];var d=["ch"];var g=[[".",","],[",","."],[","," "],[".","'"]];var c=[f,b,i,d];function j(n,l,m){this.dec=n;this.group=l;this.neg=m}function h(){for(var l=0;l<c.length;l++){localeGroup=c[l];for(var m=0;m<localeGroup.length;m++){a.put(localeGroup[m],l)}}}function e(l,r){if(a.size()==0){h()}var q=".";var o=",";var p="-";if(r==false){if(l.indexOf("_")!=-1){l=l.split("_")[1].toLowerCase()}else{if(l.indexOf("-")!=-1){l=l.split("-")[1].toLowerCase()}}}var n=a.get(l);if(n){var m=g[n];if(m){q=m[0];o=m[1]}}return new j(q,o,p)}k.fn.formatNumber=function(l,m,n){return this.each(function(){if(m==null){m=true}if(n==null){n=true}var p;if(k(this).is(":input")){p=new String(k(this).val())}else{p=new String(k(this).text())}var o=k.formatNumber(p,l);if(m){if(k(this).is(":input")){k(this).val(o)}else{k(this).text(o)}}if(n){return o}})};k.formatNumber=function(q,w){var w=k.extend({},k.fn.formatNumber.defaults,w);var l=e(w.locale.toLowerCase(),w.isFullLocale);var n=l.dec;var u=l.group;var o=l.neg;var m="0#-,.";var t="";var s=false;for(var r=0;r<w.format.length;r++){if(m.indexOf(w.format.charAt(r))==-1){t=t+w.format.charAt(r)}else{if(r==0&&w.format.charAt(r)=="-"){s=true;continue}else{break}}}var v="";for(var r=w.format.length-1;r>=0;r--){if(m.indexOf(w.format.charAt(r))==-1){v=w.format.charAt(r)+v}else{break}}w.format=w.format.substring(t.length);w.format=w.format.substring(0,w.format.length-v.length);var p=new Number(q);return k._formatNumber(p,w,v,t,s)};k._formatNumber=function(m,q,n,I,t){var q=k.extend({},k.fn.formatNumber.defaults,q);var G=e(q.locale.toLowerCase(),q.isFullLocale);var F=G.dec;var w=G.group;var l=G.neg;var z=false;if(isNaN(m)){if(q.nanForceZero==true){m=0;z=true}else{return null}}if(n=="%"){m=m*100}var B="";if(q.format.indexOf(".")>-1){var H=F;var u=q.format.substring(q.format.lastIndexOf(".")+1);if(q.round==true){m=new Number(m.toFixed(u.length))}else{var M=m.toString();M=M.substring(0,M.lastIndexOf(".")+u.length+1);m=new Number(M)}var A=m%1;var C=new String(A.toFixed(u.length));C=C.substring(C.lastIndexOf(".")+1);for(var J=0;J<u.length;J++){if(u.charAt(J)=="#"&&C.charAt(J)!="0"){H+=C.charAt(J);continue}else{if(u.charAt(J)=="#"&&C.charAt(J)=="0"){var r=C.substring(J);if(r.match("[1-9]")){H+=C.charAt(J);continue}else{break}}else{if(u.charAt(J)=="0"){H+=C.charAt(J)}}}}B+=H}else{m=Math.round(m)}var v=Math.floor(m);if(m<0){v=Math.ceil(m)}var E="";if(q.format.indexOf(".")==-1){E=q.format}else{E=q.format.substring(0,q.format.indexOf("."))}var L="";if(!(v==0&&E.substr(E.length-1)=="#")||z){var x=new String(Math.abs(v));var p=9999;if(E.lastIndexOf(",")!=-1){p=E.length-E.lastIndexOf(",")-1}var o=0;for(var J=x.length-1;J>-1;J--){L=x.charAt(J)+L;o++;if(o==p&&J!=0){L=w+L;o=0}}if(E.length>L.length){var K=E.indexOf("0");if(K!=-1){var D=E.length-K;var s=E.length-L.length-1;while(L.length<D){var y=E.charAt(s);if(y==","){y=w}L=y+L;s--}}}}if(!L&&E.indexOf("0",E.length-1)!==-1){L="0"}B=L+B;if(m<0&&t&&I.length>0){I=l+I}else{if(m<0){B=l+B}}if(!q.decimalSeparatorAlwaysShown){if(B.lastIndexOf(F)==B.length-1){B=B.substring(0,B.length-1)}}B=I+B+n;return B};k.fn.parseNumber=function(l,m,o){if(m==null){m=true}if(o==null){o=true}var p;if(k(this).is(":input")){p=new String(k(this).val())}else{p=new String(k(this).text())}var n=k.parseNumber(p,l);if(n){if(m){if(k(this).is(":input")){k(this).val(n.toString())}else{k(this).text(n.toString())}}if(o){return n}}};k.parseNumber=function(s,x){var x=k.extend({},k.fn.parseNumber.defaults,x);var m=e(x.locale.toLowerCase(),x.isFullLocale);var p=m.dec;var v=m.group;var q=m.neg;var l="1234567890.-";while(s.indexOf(v)>-1){s=s.replace(v,"")}s=s.replace(p,".").replace(q,"-");var w="";var o=false;if(s.charAt(s.length-1)=="%"||x.isPercentage==true){o=true}for(var t=0;t<s.length;t++){if(l.indexOf(s.charAt(t))>-1){w=w+s.charAt(t)}}var r=new Number(w);if(o){r=r/100;var u=w.indexOf(".");if(u!=-1){var n=w.length-u-1;r=r.toFixed(n+2)}else{r=r.toFixed(w.length-1)}}return r};k.fn.parseNumber.defaults={locale:"us",decimalSeparatorAlwaysShown:false,isPercentage:false,isFullLocale:false};k.fn.formatNumber.defaults={format:"#,###.00",locale:"us",decimalSeparatorAlwaysShown:false,nanForceZero:true,round:true,isFullLocale:false};Number.prototype.toFixed=function(l){return k._roundNumber(this,l)};k._roundNumber=function(n,m){var l=Math.pow(10,m||0);var o=String(Math.round(n*l)/l);if(m>0){var p=o.indexOf(".");if(p==-1){o+=".";p=0}else{p=o.length-(p+1)}while(p<m){o+="0";p++}}return o}})(jQuery); |
|||
File diff suppressed because one or more lines are too long
@ -0,0 +1,176 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
|
|||
<meta charset="utf-8"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|||
<meta name="description" content=""> |
|||
<meta name="author" content=""> |
|||
|
|||
<title>Apache JMeter Dashboard</title> |
|||
|
|||
<!-- Bootstrap Core CSS --> |
|||
<link href="../../sbadmin2-1.0.7/bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"> |
|||
|
|||
<!-- icone onglet --> |
|||
<link rel="icon" type="image/png" href="icon-apache.png" /> |
|||
|
|||
<!-- MetisMenu CSS --> |
|||
<link href="../../sbadmin2-1.0.7/bower_components/metisMenu/dist/metisMenu.min.css" rel="stylesheet"> |
|||
|
|||
<!-- Legends CSS --> |
|||
<link href="../css/legends.css" rel="stylesheet"> |
|||
|
|||
|
|||
<!-- Custom CSS --> |
|||
<link href="../../sbadmin2-1.0.7/dist/css/sb-admin-2.css" rel="stylesheet"> |
|||
|
|||
<!-- Custom Fonts --> |
|||
<link href="../../sbadmin2-1.0.7/bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css"> |
|||
|
|||
<!-- JQuery UI style --> |
|||
<link href="../css/jquery-ui.min.css" rel="stylesheet"> |
|||
<link href="../css/jquery-ui.structure.min.css" rel="stylesheet"> |
|||
<link href="../css/jquery-ui.theme.min.css" rel="stylesheet"> |
|||
</head> |
|||
|
|||
<body> |
|||
|
|||
<div id="wrapper"> |
|||
|
|||
<!-- Navigation --> |
|||
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0"> |
|||
<div class="navbar-header"> |
|||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> |
|||
<span class="sr-only">Toggle navigation</span> |
|||
<span class="icon-bar"></span> |
|||
<span class="icon-bar"></span> |
|||
<span class="icon-bar"></span> |
|||
</button> |
|||
<a class="navbar-brand" href="../../index.html">Apache JMeter Dashboard</a> |
|||
</div> |
|||
<!-- /.navbar-header --> |
|||
|
|||
|
|||
<div class="navbar-default sidebar" role="navigation"> |
|||
<div class="sidebar-nav navbar-collapse"> |
|||
<ul class="nav" id="side-menu"> |
|||
|
|||
<li> |
|||
<a href="../../index.html"><i class="fa fa-dashboard fa-fw"></i> Dashboard</a> |
|||
</li> |
|||
<li> |
|||
<a href="#"><i class="fa fa-bar-chart-o fa-fw"></i> Charts<span class="fa arrow"></span></a> |
|||
<ul class="nav nav-second-level"> |
|||
<li> |
|||
<a href="OverTime.html">Over Time</a> |
|||
|
|||
</li> |
|||
<li> |
|||
<a href="Throughput.html">Throughput</a> |
|||
</li> |
|||
<li> |
|||
<a href="ResponseTimes.html">Response Times</a> |
|||
</li> |
|||
|
|||
</ul> |
|||
<!-- /.nav-second-level --> |
|||
</li> |
|||
|
|||
<li> |
|||
<a href="#"><i class="fa fa-bar-chart-o fa-fw"></i> Customs Graphs<span class="fa arrow"></span></a> |
|||
<ul class="nav nav-second-level"> |
|||
<li> |
|||
<a href="CustomsGraphs.html">Over Time<span class="fa arrow"></span></a> |
|||
<ul class="nav nav-third-level in" id="submenu"> |
|||
</ul> |
|||
<li> |
|||
</ul> |
|||
</li> |
|||
|
|||
</ul> |
|||
</div> |
|||
<!-- /.sidebar-collapse --> |
|||
</div> |
|||
<!-- /.navbar-static-side --> |
|||
</nav> |
|||
|
|||
<div id="page-wrapper"> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
<div class="panel panel-default" > |
|||
<div class="panel-heading" style="text-align:center;"> |
|||
<p class="dashboard-title">Test and Report information</p> |
|||
</div> |
|||
<div class="panel-body"> |
|||
<table id="generalInfos" class="table table-bordered table-condensed " > |
|||
<tr> |
|||
<td>File:</td> |
|||
<td>"WithAbp.jtl"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Start Time:</td> |
|||
<td>"12/31/23, 11:45 AM"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>End Time:</td> |
|||
<td>"12/31/23, 11:46 AM"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Filter for display:</td> |
|||
<td>""</td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.row --> |
|||
<div class="row" id="graphContainer"> |
|||
|
|||
</div> |
|||
<!-- /.row --> |
|||
</div> |
|||
<!-- /#page-wrapper --> |
|||
|
|||
</div> |
|||
<!-- /#wrapper --> |
|||
|
|||
<!-- jQuery --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/jquery/dist/jquery.min.js"></script> |
|||
|
|||
<!-- Bootstrap Core JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> |
|||
|
|||
<!-- Metis Menu Plugin JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/metisMenu/dist/metisMenu.min.js"></script> |
|||
|
|||
<!-- Flot Charts JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/excanvas.min.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.pie.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.resize.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.canvas.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.navigate.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.time.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.selection.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot.tooltip/js/jquery.flot.tooltip.min.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot-axislabels/jquery.flot.axislabels.js"></script> |
|||
<script src="../js/hashtable.js"></script> |
|||
<script src="../js/jquery.numberformatter-1.2.3.min.js"></script> |
|||
<script src="../js/curvedLines.js"></script> |
|||
<script src="../js/dashboard-commons.js"></script> |
|||
<script src="../js/customGraph.js"></script> |
|||
<script src="../js/jquery-ui.min.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.threshold.js"></script> |
|||
<!-- Custom Theme JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/dist/js/sb-admin-2.js"></script> |
|||
<script src="../js/jquery.cookie.js"></script> |
|||
|
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.symbol.js"></script> |
|||
|
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,512 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
|
|||
<meta charset="utf-8"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|||
<meta name="description" content=""> |
|||
<meta name="author" content=""> |
|||
|
|||
<title>Apache JMeter Dashboard</title> |
|||
|
|||
<!-- Bootstrap Core CSS --> |
|||
<link href="../../sbadmin2-1.0.7/bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"> |
|||
|
|||
<!-- icone onglet --> |
|||
<link rel="icon" type="image/png" href="icon-apache.png" /> |
|||
|
|||
<!-- MetisMenu CSS --> |
|||
<link href="../../sbadmin2-1.0.7/bower_components/metisMenu/dist/metisMenu.min.css" rel="stylesheet"> |
|||
|
|||
<!-- Legends CSS --> |
|||
<link href="../css/legends.css" rel="stylesheet"> |
|||
|
|||
|
|||
<!-- Custom CSS --> |
|||
<link href="../../sbadmin2-1.0.7/dist/css/sb-admin-2.css" rel="stylesheet"> |
|||
|
|||
<!-- Custom Fonts --> |
|||
<link href="../../sbadmin2-1.0.7/bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css"> |
|||
|
|||
<!-- JQuery UI style --> |
|||
<link href="../css/jquery-ui.min.css" rel="stylesheet"> |
|||
<link href="../css/jquery-ui.structure.min.css" rel="stylesheet"> |
|||
<link href="../css/jquery-ui.theme.min.css" rel="stylesheet"> |
|||
</head> |
|||
|
|||
<body> |
|||
|
|||
<div id="wrapper"> |
|||
|
|||
<!-- Navigation --> |
|||
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0"> |
|||
<div class="navbar-header"> |
|||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> |
|||
<span class="sr-only">Toggle navigation</span> |
|||
<span class="icon-bar"></span> |
|||
<span class="icon-bar"></span> |
|||
<span class="icon-bar"></span> |
|||
</button> |
|||
<a class="navbar-brand" href="../../index.html">Apache JMeter Dashboard</a> |
|||
</div> |
|||
<!-- /.navbar-header --> |
|||
|
|||
|
|||
<div class="navbar-default sidebar" role="navigation"> |
|||
<div class="sidebar-nav navbar-collapse"> |
|||
<ul class="nav" id="side-menu"> |
|||
|
|||
<li> |
|||
<a href="../../index.html"><i class="fa fa-dashboard fa-fw"></i> Dashboard</a> |
|||
</li> |
|||
<li> |
|||
<a href="#"><i class="fa fa-bar-chart-o fa-fw"></i> Charts<span class="fa arrow"></span></a> |
|||
<ul class="nav nav-second-level"> |
|||
<li> |
|||
<a href="OverTime.html">Over Time<span class="fa arrow"></span></a> |
|||
<ul class="nav nav-third-level in" id="submenu"> |
|||
<li> |
|||
<a href="OverTime.html#responseTimesOverTime" onclick="$('#bodyResponseTimeOverTime').collapse('show');"> |
|||
Response Times Over Time |
|||
</a> |
|||
</li> |
|||
<li> |
|||
<a href="OverTime.html#responseTimePercentilesOverTime" onclick="$('#bodyResponseTimePercentilesOverTime').collapse('show');"> |
|||
Response Time Percentiles Over Time (successful responses) |
|||
</a> |
|||
</li> |
|||
<li> |
|||
<a href="OverTime.html#activeThreadsOverTime" onclick="$('#bodyActiveThreadsOverTime').collapse('show');"> |
|||
Active Threads Over Time |
|||
</a> |
|||
</li> |
|||
<li> |
|||
<a href="OverTime.html#bytesThroughputOverTime" onclick="$('#bodyBytesThroughputOverTime').collapse('show');"> |
|||
Bytes Throughput Over Time |
|||
</a> |
|||
</li> |
|||
<li> |
|||
<a href="OverTime.html#latenciesOverTime" onclick="$('#bodyLatenciesOverTime').collapse('show');"> |
|||
Latencies Over Time |
|||
</a> |
|||
</li> |
|||
<li> |
|||
<a href="OverTime.html#connectTimeOverTime" onclick="$('#bodyConnectTimeOverTime').collapse('show');"> |
|||
Connect Time Over Time |
|||
</a> |
|||
</li> |
|||
</ul> |
|||
</li> |
|||
<li> |
|||
<a href="Throughput.html">Throughput</a> |
|||
</li> |
|||
<li> |
|||
<a href="ResponseTimes.html">Response Times</a> |
|||
</li> |
|||
</ul> |
|||
<!-- /.nav-second-level --> |
|||
</li> |
|||
|
|||
<li> |
|||
<a href="#"><i class="fa fa-bar-chart-o fa-fw"></i>Customs Graphs<span class="fa arrow"></span></a> |
|||
<ul class="nav nav-second-level"> |
|||
<li> |
|||
<a href="CustomsGraphs.html">Over Time</a> |
|||
</li> |
|||
</ul> |
|||
</li> |
|||
|
|||
</ul> |
|||
</div> |
|||
<!-- /.sidebar-collapse --> |
|||
</div> |
|||
<!-- /.navbar-static-side --> |
|||
</nav> |
|||
<!--modal--> |
|||
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> |
|||
<div class="modal-dialog" style="width:90%;height: 5%;"> |
|||
<div class="modal-content"> |
|||
<div class="modal-header"> |
|||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> |
|||
<h4 class="modal-title" id="myModalLabel">Zoom</h4> |
|||
</div> |
|||
<div class="modal-body"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flot-modal-content"></div> |
|||
</div> |
|||
</div> |
|||
<div class="modal-footer" id="modalFooter"> |
|||
<p id="legendModal" hidden></p> |
|||
<ul id="choicesModal" class="legend"></ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="page-wrapper"> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
<div class="panel panel-default" > |
|||
<div class="panel-heading" style="text-align:center;"> |
|||
<p class="dashboard-title">Test and Report information</p> |
|||
</div> |
|||
<div class="panel-body"> |
|||
<table id="generalInfos" class="table table-bordered table-condensed " > |
|||
<tr> |
|||
<td>File:</td> |
|||
<td>"WithAbp.jtl"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Start Time:</td> |
|||
<td>"12/31/23, 11:45 AM"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>End Time:</td> |
|||
<td>"12/31/23, 11:46 AM"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Filter for display:</td> |
|||
<td>""</td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.row --> |
|||
<div class="row" id="graphContainer"> |
|||
|
|||
<!-- /.col-lg-6 --> |
|||
<div class="col-lg-12 portlet" id="responseTimesOverTime"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw" ></i> |
|||
<span type="button" class="span-title dropdown-toggle click-title" data-toggle="collapse" href="#bodyResponseTimeOverTime" aria-expanded="true" aria-controls="bodyResponseTimeOverTime">Response Times Over Time</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#bodyResponseTimeOverTime" onClick="checkAll('choicesResponseTimesOverTime');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#bodyResponseTimeOverTime" onClick="uncheckAll('choicesResponseTimesOverTime');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#bodyResponseTimeOverTime" onclick="exportToPNG('flotResponseTimesOverTime', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyResponseTimeOverTime" aria-expanded="true" aria-controls="bodyResponseTimeOverTime"> |
|||
<i class="fa fa-chevron-up"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse in portlet-content" id="bodyResponseTimeOverTime"> |
|||
<div class="panel-body" id="collapseResponseTimesOverTime"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotResponseTimesOverTime" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewResponseTimesOverTime" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerResponseTimesOverTime"> |
|||
<p id="legendResponseTimesOverTime" hidden></p> |
|||
<ul id="choicesResponseTimesOverTime" class="legend"></ul> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
<!-- /.col-lg-12 --> |
|||
|
|||
<!-- /.col-lg-12 --> |
|||
<div class="col-lg-12 portlet" id="responseTimePercentilesOverTime"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"></i> |
|||
<span type="button" class="dropdown-toggle click-title span-title" data-toggle="collapse" href="#bodyResponseTimePercentilesOverTime" aria-expanded="true" aria-controls="bodyResponseTimePercentilesOverTime">Response Time Percentiles Over Time (successful responses)</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#responseTimePercentilesOverTime" onClick="checkAll('choicesResponseTimePercentilesOverTime');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#responseTimePercentilesOverTime" onClick="uncheckAll('choicesResponseTimePercentilesOverTime');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#responseTimePercentilesOverTime" onclick="exportToPNG('flotResponseTimePercentilesOverTime', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyResponseTimePercentilesOverTime" aria-expanded="true" aria-controls="bodyResponseTimePercentilesOverTime"> |
|||
<i class="fa fa-chevron-down"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse out portlet-content" id="bodyResponseTimePercentilesOverTime"> |
|||
<div class="panel-body" id="collapseConnectTime"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotResponseTimePercentilesOverTime" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewResponseTimePercentilesOverTime" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerResponseTimePercentilesOverTime"> |
|||
<p id="legendResponseTimePercentilesOverTime" hidden></p> |
|||
<ul id="choicesResponseTimePercentilesOverTime" class="legend"></ul> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
|
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
<!-- /.col-lg-12 --> |
|||
|
|||
<div class="col-lg-12 portlet" id="activeThreadsOverTime"> |
|||
<div class="panel panel-default" > |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"> </i> <span type="button" class="span-title dropdown-toggle click-title" data-toggle="collapse" href="#bodyActiveThreadsOverTime" aria-expanded="true" aria-controls="bodyActiveThreadsOverTime">Active Threads Over Time</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="drag btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#activeThreadsOverTime" onClick="checkAll('choicesActiveThreadsOverTime');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#activeThreadsOverTime" onClick="uncheckAll('choicesActiveThreadsOverTime');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#activeThreadsOverTime" onclick="exportToPNG('flotActiveThreadsOverTime', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyActiveThreadsOverTime" aria-expanded="true" aria-controls="bodyActiveThreadsOverTime"> |
|||
<i class="fa fa-chevron-down"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse out portlet-content" id="bodyActiveThreadsOverTime"> |
|||
<div class="panel-body" id="collapseActiveThreadsOverTime"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotActiveThreadsOverTime" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewActiveThreadsOverTime" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerActiveThreadsOverTime"> |
|||
<p id="legendActiveThreadsOverTime" hidden></p> |
|||
<ul id="choicesActiveThreadsOverTime" class="legend"> |
|||
|
|||
</ul> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
|
|||
|
|||
<div class="col-lg-12 portlet" id="bytesThroughputOverTime"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw" ></i> <span type="button" class="dropdown-toggle click-title span-title" data-toggle="collapse" href="#bodyBytesThroughputOverTime" aria-expanded="true" aria-controls="bodyBytesThroughputOverTime">Bytes Throughput Over Time</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#bytesThroughputOverTime" onClick="checkAll('choicesBytesThroughputOverTime');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#bytesThroughputOverTime" onClick="uncheckAll('choicesBytesThroughputOverTime');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#bytesThroughputOverTime" onclick="exportToPNG('flotBytesThroughputOverTime', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyBytesThroughputOverTime" aria-expanded="true" aria-controls="bodyBytesThroughputOverTime"> |
|||
<i class="fa fa-chevron-down"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse out portlet-content" id="bodyBytesThroughputOverTime"> |
|||
<div class="panel-body " id="collapseBytes"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotBytesThroughputOverTime" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewBytesThroughputOverTime" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
<div class="panel-footer" id="footerBytesThroughputOverTime"> |
|||
<p id="legendBytesThroughputOverTime" hidden></p> |
|||
<ul id="choicesBytesThroughputOverTime" class="legend"></ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
|
|||
<!-- /.col-lg-6 --> |
|||
<div class="col-lg-12 portlet" id="latenciesOverTime"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"></i> |
|||
<span type="button" class="dropdown-toggle click-title span-title" data-toggle="collapse" href="#bodyLatenciesOverTime" aria-expanded="true" aria-controls="bodyLatenciesOverTime">Latencies Over Time</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#latenciesOverTime" onClick="checkAll('choicesLatenciesOverTime');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#latenciesOverTime" onClick="uncheckAll('choicesLatenciesOverTime');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#latenciesOverTime" onclick="exportToPNG('flotLatenciesOverTime', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyLatenciesOverTime" aria-expanded="true" aria-controls="bodyLatenciesOverTime"> |
|||
<i class="fa fa-chevron-down"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse out portlet-content" id="bodyLatenciesOverTime"> |
|||
<div class="panel-body" id="collapseLatencies"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotLatenciesOverTime" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewLatenciesOverTime" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerLatenciesOverTime"> |
|||
<p id="legendLatenciesOverTime" hidden></p> |
|||
<ul id="choicesLatenciesOverTime" class="legend"></ul> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
|
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
|
|||
<!-- /.col-lg-6 --> |
|||
<div class="col-lg-12 portlet" id="connectTimeOverTime"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"></i> |
|||
<span type="button" class="dropdown-toggle click-title span-title" data-toggle="collapse" href="#bodyConnectTimeOverTime" aria-expanded="true" aria-controls="bodyConnectTimeOverTime">Connect Time Over Time</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#connectTimeOverTime" onClick="checkAll('choicesConnectTimeOverTime');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#connectTimeOverTime" onClick="uncheckAll('choicesConnectTimeOverTime');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#connectTimeOverTime" onclick="exportToPNG('flotConnectTimeOverTime', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyConnectTimeOverTime" aria-expanded="true" aria-controls="bodyConnectTimeOverTime"> |
|||
<i class="fa fa-chevron-down"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse out portlet-content" id="bodyConnectTimeOverTime"> |
|||
<div class="panel-body" id="collapseConnectTime"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotConnectTimeOverTime" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewConnectTimeOverTime" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerConnectTimeOverTime"> |
|||
<p id="legendConnectTimeOverTime" hidden></p> |
|||
<ul id="choicesConnectTimeOverTime" class="legend"></ul> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
|
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
<!-- /.col-lg-6 --> |
|||
</div> |
|||
<!-- /.row --> |
|||
</div> |
|||
<!-- /#page-wrapper --> |
|||
|
|||
</div> |
|||
<!-- /#wrapper --> |
|||
|
|||
<!-- jQuery --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/jquery/dist/jquery.min.js"></script> |
|||
|
|||
<!-- Bootstrap Core JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> |
|||
|
|||
<!-- Metis Menu Plugin JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/metisMenu/dist/metisMenu.min.js"></script> |
|||
|
|||
<!-- Flot Charts JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/excanvas.min.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.pie.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.resize.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.canvas.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.time.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.selection.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot.tooltip/js/jquery.flot.tooltip.min.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot-axislabels/jquery.flot.axislabels.js"></script> |
|||
<script src="../js/jquery.flot.stack.js"></script> |
|||
<script src="../js/hashtable.js"></script> |
|||
<script src="../js/jquery.numberformatter-1.2.3.min.js"></script> |
|||
<script src="../js/curvedLines.js"></script> |
|||
<script src="../js/dashboard-commons.js"></script> |
|||
<script src="../js/graph.js"></script> |
|||
<script src="../js/jquery-ui.min.js"></script> |
|||
<script src="../js/jquery.cookie.js"></script> |
|||
<!-- Custom Theme JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/dist/js/sb-admin-2.js"></script> |
|||
|
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,373 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
|
|||
<meta charset="utf-8"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|||
<meta name="description" content=""> |
|||
<meta name="author" content=""> |
|||
|
|||
<title>Apache JMeter Dashboard</title> |
|||
|
|||
<!-- Bootstrap Core CSS --> |
|||
<link href="../../sbadmin2-1.0.7/bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"> |
|||
|
|||
<!-- icone onglet --> |
|||
<link rel="icon" type="image/png" href="icon-apache.png" /> |
|||
|
|||
<!-- MetisMenu CSS --> |
|||
<link href="../../sbadmin2-1.0.7/bower_components/metisMenu/dist/metisMenu.min.css" rel="stylesheet"> |
|||
|
|||
<!-- Legends CSS --> |
|||
<link href="../css/legends.css" rel="stylesheet"> |
|||
|
|||
|
|||
<!-- Custom CSS --> |
|||
<link href="../../sbadmin2-1.0.7/dist/css/sb-admin-2.css" rel="stylesheet"> |
|||
|
|||
<!-- Custom Fonts --> |
|||
<link href="../../sbadmin2-1.0.7/bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css"> |
|||
|
|||
<!-- JQuery UI style --> |
|||
<link href="../css/jquery-ui.min.css" rel="stylesheet"> |
|||
<link href="../css/jquery-ui.structure.min.css" rel="stylesheet"> |
|||
<link href="../css/jquery-ui.theme.min.css" rel="stylesheet"> |
|||
</head> |
|||
|
|||
<body> |
|||
|
|||
<div id="wrapper"> |
|||
|
|||
<!-- Navigation --> |
|||
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0"> |
|||
<div class="navbar-header"> |
|||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> |
|||
<span class="sr-only">Toggle navigation</span> |
|||
<span class="icon-bar"></span> |
|||
<span class="icon-bar"></span> |
|||
<span class="icon-bar"></span> |
|||
</button> |
|||
<a class="navbar-brand" href="../../index.html">Apache JMeter Dashboard</a> |
|||
</div> |
|||
<!-- /.navbar-header --> |
|||
|
|||
|
|||
<div class="navbar-default sidebar" role="navigation"> |
|||
<div class="sidebar-nav navbar-collapse"> |
|||
<ul class="nav" id="side-menu"> |
|||
|
|||
<li> |
|||
<a href="../../index.html"><i class="fa fa-dashboard fa-fw"></i> Dashboard</a> |
|||
</li> |
|||
<li> |
|||
<a href="#"><i class="fa fa-bar-chart-o fa-fw"></i> Charts<span class="fa arrow"></span></a> |
|||
<ul class="nav nav-second-level"> |
|||
<li> |
|||
<a href="OverTime.html">Over Time</a> |
|||
|
|||
</li> |
|||
<li> |
|||
<a href="Throughput.html">Throughput</a> |
|||
</li> |
|||
<li> |
|||
<a href="ResponseTimes.html">Response Times<span class="fa arrow"></span></a> |
|||
|
|||
<ul class="nav nav-third-level in" id="submenu"> |
|||
<li> |
|||
<a href="ResponseTimes.html#responseTimePercentiles" onclick="$('#bodyResponseTimePercentiles').collapse('show');"> |
|||
Response Time Percentiles |
|||
</a> |
|||
</li> |
|||
<li> |
|||
<a href="ResponseTimes.html#syntheticResponseTimeDistribution" onclick="$('#bodySyntheticResponseTimeDistribution').collapse('show');"> |
|||
Response Time Overview |
|||
</a> |
|||
</li> |
|||
<li> |
|||
<a href="ResponseTimes.html#timeVsThreads" onclick="$('#bodyTimeVsThreads').collapse('show');">Time Vs Threads</a> |
|||
</li> |
|||
<li> |
|||
<a href="ResponseTimes.html#responseTimeDistribution" onclick="$('#bodyResponseTimeDistribution').collapse('show');"> |
|||
Response Time Distribution |
|||
</a> |
|||
</li> |
|||
</ul> |
|||
|
|||
</li> |
|||
</ul> |
|||
<!-- /.nav-second-level --> |
|||
</li> |
|||
<li> |
|||
<a href="#"><i class="fa fa-bar-chart-o fa-fw"></i> Customs Graphs<span class="fa arrow"></span></a> |
|||
<ul class="nav nav-second-level"> |
|||
<li> |
|||
<a href="CustomsGraphs.html">Over Time</a> |
|||
</li> |
|||
</ul> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<!-- /.sidebar-collapse --> |
|||
</div> |
|||
<!-- /.navbar-static-side --> |
|||
</nav> |
|||
|
|||
<div id="page-wrapper"> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
<div class="panel panel-default" > |
|||
<div class="panel-heading" style="text-align:center;"> |
|||
<p class="dashboard-title">Test and Report information</p> |
|||
</div> |
|||
<div class="panel-body"> |
|||
<table id="generalInfos" class="table table-bordered table-condensed " > |
|||
<tr> |
|||
<td>File:</td> |
|||
<td>"WithAbp.jtl"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Start Time:</td> |
|||
<td>"12/31/23, 11:45 AM"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>End Time:</td> |
|||
<td>"12/31/23, 11:46 AM"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Filter for display:</td> |
|||
<td>""</td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.row --> |
|||
<div class="row" id="graphContainer"> |
|||
<div class="col-lg-12 portlet" id="responseTimePercentiles"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"></i> <span type="button" class="span-title dropdown-toggle click-title" data-toggle="collapse" href="#bodyResponseTimePercentiles" aria-expanded="true" aria-controls="bodyResponseTimePercentiles">Response Time Percentiles</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="drag btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#responseTimePercentiles" onClick="checkAll('choicesResponseTimePercentiles');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#responseTimePercentiles" onClick="uncheckAll('choicesResponseTimePercentiles');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#responseTimePercentiles" onclick="exportToPNG('flotResponseTimesPercentiles', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyResponseTimePercentiles" aria-expanded="true" aria-controls="bodyResponseTimePercentiles"> |
|||
<i class="fa fa-chevron-up"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse in portlet-content" id="bodyResponseTimePercentiles"> |
|||
<div class="panel-body" id="collapseResponseTimePercentiles"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotResponseTimesPercentiles" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewResponseTimesPercentiles" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerPercentiles"> |
|||
<p id="legendResponseTimePercentiles" hidden></p> |
|||
<ul id="choicesResponseTimePercentiles" class="legend"> |
|||
|
|||
</ul> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
<div class="col-lg-12 portlet" id="syntheticResponseTimeDistribution"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"></i> <span type="button" class="dropdown-toggle click-title span-title" data-toggle="collapse" href="#bodySyntheticResponseTimeDistribution" aria-expanded="true" aria-controls="bodySyntheticResponseTimeDistribution">Response Time Overview</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="drag btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#syntheticResponseTimeDistribution" onClick="checkAll('choicesSyntheticResponseTimeDistribution');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#syntheticResponseTimeDistribution" onClick="uncheckAll('choicesSyntheticResponseTimeDistribution');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#syntheticResponseTimeDistribution" onclick="exportToPNG('flotSyntheticResponseTimesDistribution', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodySyntheticResponseTimeDistribution" aria-expanded="true" aria-controls="bodySyntheticResponseTimeDistribution"> |
|||
<i class="fa fa-chevron-down"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse out portlet-content" id="bodySyntheticResponseTimeDistribution"> |
|||
<div class="panel-body" id="collapseSyntheticResponseTimeDistribution"> |
|||
|
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotSyntheticResponseTimeDistribution"></div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerSyntheticResponseTimeDistribution"> |
|||
<p id="legendSyntheticResponseTimeDistribution" hidden></p> |
|||
<ul id="choicesSyntheticResponseTimeDistribution" class="legend"> |
|||
|
|||
</ul> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
<div class="col-lg-12 portlet" id="timeVsThreads"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"></i><span type="button" class="span-title dropdown-toggle click-title" data-toggle="collapse" href="#bodyTimeVsThreads" aria-expanded="true" aria-controls="bodyTimeVsThreads">Time Vs Threads</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="drag btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#timeVsThreads" onClick="checkAll('choicesTimeVsThreads');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#timeVsThreads" onClick="uncheckAll('choicesTimeVsThreads');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#timeVsThreads" onclick="exportToPNG('flotTimesVsThreads', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyTimeVsThreads" aria-expanded="true" aria-controls="bodyTimeVsThreads"> |
|||
<i class="fa fa-chevron-down"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse out portlet-content" id="bodyTimeVsThreads"> |
|||
<div class="panel-body" id="collapseTimeVsThreads"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotTimesVsThreads" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewTimesVsThreads" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerTimeVsThreads"> |
|||
<p id="legendTimeVsThreads" hidden></p> |
|||
<ul id="choicesTimeVsThreads" class="legend"></ul> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
<div class="col-lg-12 portlet" id="responseTimeDistribution"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"></i> <span type="button" class="dropdown-toggle click-title span-title" data-toggle="collapse" href="#bodyResponseTimeDistribution" aria-expanded="true" aria-controls="bodyResponseTimeDistribution">Response Time Distribution</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="drag btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#responseTimeDistribution" onClick="checkAll('choicesResponseTimeDistribution');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#responseTimeDistribution" onClick="uncheckAll('choicesResponseTimeDistribution');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#responseTimeDistribution" onclick="exportToPNG('flotResponseTimeDistribution', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyResponseTimeDistribution" aria-expanded="true" aria-controls="bodyResponseTimeDistribution"> |
|||
<i class="fa fa-chevron-down"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse out portlet-content" id="bodyResponseTimeDistribution"> |
|||
<div class="panel-body" id="collapseResponseTimeDistribution"> |
|||
|
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotResponseTimeDistribution"></div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerResponseTimeDistribution"> |
|||
<p id="legendResponseTimeDistribution" hidden></p> |
|||
<ul id="choicesResponseTimeDistribution" class="legend"> |
|||
|
|||
</ul> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
|
|||
<!-- /.col-lg-6 --> |
|||
</div> |
|||
<!-- /.row --> |
|||
</div> |
|||
<!-- /#page-wrapper --> |
|||
|
|||
</div> |
|||
<!-- /#wrapper --> |
|||
|
|||
<!-- jQuery --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/jquery/dist/jquery.min.js"></script> |
|||
|
|||
<!-- Bootstrap Core JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> |
|||
|
|||
<!-- Metis Menu Plugin JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/metisMenu/dist/metisMenu.min.js"></script> |
|||
|
|||
<!-- Flot Charts JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/excanvas.min.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.pie.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.resize.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.canvas.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.navigate.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.time.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.selection.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot.tooltip/js/jquery.flot.tooltip.min.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot-axislabels/jquery.flot.axislabels.js"></script> |
|||
<script src="../js/hashtable.js"></script> |
|||
<script src="../js/jquery.numberformatter-1.2.3.min.js"></script> |
|||
<script src="../js/curvedLines.js"></script> |
|||
<script src="../js/dashboard-commons.js"></script> |
|||
<script src="../js/graph.js"></script> |
|||
<script src="../js/jquery-ui.min.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.threshold.js"></script> |
|||
<!-- Custom Theme JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/dist/js/sb-admin-2.js"></script> |
|||
<script src="../js/jquery.cookie.js"></script> |
|||
|
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.symbol.js"></script> |
|||
|
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,463 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
|
|||
<meta charset="utf-8"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|||
<meta name="description" content=""> |
|||
<meta name="author" content=""> |
|||
|
|||
<title>Apache JMeter Dashboard</title> |
|||
|
|||
<!-- Bootstrap Core CSS --> |
|||
<link href="../../sbadmin2-1.0.7/bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"> |
|||
|
|||
<!-- icon --> |
|||
<link rel="icon" type="image/png" href="icon-apache.png" /> |
|||
|
|||
<!-- MetisMenu CSS --> |
|||
<link href="../../sbadmin2-1.0.7/bower_components/metisMenu/dist/metisMenu.min.css" rel="stylesheet"> |
|||
|
|||
<!-- Legends CSS --> |
|||
<link href="../css/legends.css" rel="stylesheet"> |
|||
|
|||
|
|||
<!-- Custom CSS --> |
|||
<link href="../../sbadmin2-1.0.7/dist/css/sb-admin-2.css" rel="stylesheet"> |
|||
|
|||
<!-- Custom Fonts --> |
|||
<link href="../../sbadmin2-1.0.7/bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css"> |
|||
|
|||
<!-- JQuery UI style --> |
|||
<link href="../css/jquery-ui.min.css" rel="stylesheet"> |
|||
<link href="../css/jquery-ui.structure.min.css" rel="stylesheet"> |
|||
<link href="../css/jquery-ui.theme.min.css" rel="stylesheet"> |
|||
</head> |
|||
|
|||
<body> |
|||
|
|||
<div id="wrapper"> |
|||
|
|||
<!-- Navigation --> |
|||
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0"> |
|||
<div class="navbar-header"> |
|||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> |
|||
<span class="sr-only">Toggle navigation</span> |
|||
<span class="icon-bar"></span> |
|||
<span class="icon-bar"></span> |
|||
<span class="icon-bar"></span> |
|||
</button> |
|||
<a class="navbar-brand" href="../../index.html">Apache JMeter Dashboard</a> |
|||
</div> |
|||
<!-- /.navbar-header --> |
|||
|
|||
|
|||
<div class="navbar-default sidebar" role="navigation"> |
|||
<div class="sidebar-nav navbar-collapse"> |
|||
<ul class="nav" id="side-menu"> |
|||
|
|||
<li> |
|||
<a href="../../index.html"><i class="fa fa-dashboard fa-fw"></i> Dashboard</a> |
|||
</li> |
|||
<li> |
|||
<a href="#"><i class="fa fa-bar-chart-o fa-fw"></i> Charts<span class="fa arrow"></span></a> |
|||
<ul class="nav nav-second-level"> |
|||
<li> |
|||
<a href="OverTime.html">Over Time</a> |
|||
</li> |
|||
<li> |
|||
<a href="Throughput.html">Throughput<span class="fa arrow"></span></a> |
|||
<ul class="nav nav-third-level in" id="submenu"> |
|||
<li> |
|||
<a href="Throughput.html#hitsPerSecond" onclick="$('#bodyHitsPerSecond').collapse('show');">Hits Per Second</a> |
|||
</li> |
|||
|
|||
<li> |
|||
<a href="Throughput.html#codesPerSecond" onclick="$('#bodyCodesPerSecond').collapse('show');">Codes Per Second</a> |
|||
</li> |
|||
<li> |
|||
<a href="Throughput.html#transactionsPerSecond" onclick="$('#bodyTransactionsPerSecond').collapse('show');">Transactions Per Second</a> |
|||
</li> |
|||
<li> |
|||
<a href="Throughput.html#totalTPS" onclick="$('#bodyTotalTPS').collapse('show');">Total Transactions Per Second</a> |
|||
</li> |
|||
<li> |
|||
<a href="Throughput.html#responseTimeVsRequest" onclick="$('#bodyResponseTimeVsRequest').collapse('show');">Response Time Vs Request</a> |
|||
</li> |
|||
<li> |
|||
<a href="Throughput.html#latencyVsRequest" onclick="$('#bodyLatenciesVsRequest').collapse('show');">Latency Vs Request</a> |
|||
</li> |
|||
</ul> |
|||
</li> |
|||
<li> |
|||
<a href="ResponseTimes.html">Response Times</a> |
|||
</li> |
|||
</ul> |
|||
<!-- /.nav-second-level --> |
|||
</li> |
|||
|
|||
<li> |
|||
<a href="#"><i class="fa fa-bar-chart-o fa-fw"></i> Customs Graphs<span class="fa arrow"></span></a> |
|||
<ul class="nav nav-second-level"> |
|||
<li> |
|||
<a href="CustomsGraphs.html">Over Time</a> |
|||
</li> |
|||
</ul> |
|||
</li> |
|||
|
|||
</ul> |
|||
</div> |
|||
<!-- /.sidebar-collapse --> |
|||
</div> |
|||
<!-- /.navbar-static-side --> |
|||
</nav> |
|||
|
|||
<div id="page-wrapper"> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading" style="text-align:center;"> |
|||
<p class="dashboard-title">Test and Report information</p> |
|||
</div> |
|||
<div class="panel-body"> |
|||
<table id="generalInfos" class="table table-bordered table-condensed " > |
|||
<tr> |
|||
<td>File:</td> |
|||
<td>"WithAbp.jtl"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Start Time:</td> |
|||
<td>"12/31/23, 11:45 AM"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>End Time:</td> |
|||
<td>"12/31/23, 11:46 AM"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Filter for display:</td> |
|||
<td>""</td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.row --> |
|||
<div class="row" id="graphContainer"> |
|||
|
|||
<!-- /.col-lg-12 --> |
|||
|
|||
<div class="col-lg-12 portlet" id="hitsPerSecond"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"> </i> <span type="button" class="dropdown-toggle click-title span-title" data-toggle="collapse" href="#bodyHitsPerSecond" aria-expanded="true" aria-controls="bodyHitsPerSecond">Hits Per Second</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#hitsPerSecond" onClick="checkAll('choicesHitsPerSecond');">Display samples</a> |
|||
</li> |
|||
<li><a href="#hitsPerSecond" onClick="uncheckAll('choicesHitsPerSecond');">Hide samples</a> |
|||
</li> |
|||
<li><a href="#hitsPerSecond" onclick="exportToPNG('flotHitsPerSecond', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyHitsPerSecond" aria-expanded="true" aria-controls="bodyHitsPerSecond"> |
|||
<i class="fa fa-chevron-up"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse in portlet-content" id="bodyHitsPerSecond"> |
|||
<div class="panel-body" id="collapseHitsPerSecond" > |
|||
<div class="flot-chart" > |
|||
<div class="flot-chart-content" id="flotHitsPerSecond" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewHitsPerSecond" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerHitsPerSecond"> |
|||
<p id="legendHitsPerSecond" hidden></p> |
|||
<ul id="choicesHitsPerSecond" class="legend"></ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- codesPerSecond --> |
|||
|
|||
<div class="col-lg-12 portlet" id="codesPerSecond"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"></i> |
|||
<span type="button" class="dropdown-toggle click-title span-title" data-toggle="collapse" href="#bodyCodesPerSecond" aria-expanded="true" aria-controls="bodyCodesPerSecond">Codes Per Second</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#codesPerSecond" onClick="checkAll('choicesCodesPerSecond');">Display samples</a> |
|||
</li> |
|||
<li><a href="#codesPerSecond" onClick="uncheckAll('choicesCodesPerSecond');">Hide samples</a> |
|||
</li> |
|||
<li><a href="#codesPerSecond" onclick="exportToPNG('flotCodesPerSecond', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyCodesPerSecond" aria-expanded="true" aria-controls="bodyCodesPerSecond"> |
|||
<i class="fa fa-chevron-down"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
|
|||
<div class="collapse out portlet-content" id="bodyCodesPerSecond"> |
|||
<div class="panel-body" id="collapseCodesPerSecond"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotCodesPerSecond" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewCodesPerSecond" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerCodesPerSecond"> |
|||
<p id="legendCodesPerSecond" hidden></p> |
|||
<ul id="choicesCodesPerSecond" class="legend"></ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-12 portlet" id="transactionsPerSecond"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"> </i> <span type="button" class="dropdown-toggle click-title span-title" data-toggle="collapse" href="#bodyTransactionsPerSecond" aria-expanded="true" aria-controls="bodyTransactionsPerSecond">Transactions Per Second</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#transactionsPerSecond" onClick="checkAll('choicesTransactionsPerSecond');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#transactionsPerSecond" onClick="uncheckAll('choicesTransactionsPerSecond');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#transactionsPerSecond" onclick="exportToPNG('flotTransactionsPerSecond', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyTransactionsPerSecond" aria-expanded="true" aria-controls="bodyTransactionsPerSecond"> |
|||
<i class="fa fa-chevron-down"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse out portlet-content" id="bodyTransactionsPerSecond"> |
|||
<div class="panel-body" id="collapseTransactionsPerSecond"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotTransactionsPerSecond" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewTransactionsPerSecond" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerTransactionsPerSecond"> |
|||
<p id="legendTransactionsPerSecond" hidden></p> |
|||
<ul id="choicesTransactionsPerSecond" class="legend"></ul> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
|
|||
<div class="col-lg-12 portlet" id="totalTPS"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"> </i> <span type="button" class="dropdown-toggle click-title span-title" data-toggle="collapse" href="#bodyTotalTPS" aria-expanded="true" aria-controls="bodyTotalTPS">Total Transactions Per Second</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#totalTPS" onClick="checkAll('choicesTotalTPS');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#totalTPS" onClick="uncheckAll('choicesTotalTPS');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#totalTPS" onclick="exportToPNG('flotTotalTPS', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyTotalTPS" aria-expanded="true" aria-controls="bodyTotalTPS"> |
|||
<i class="fa fa-chevron-down"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse out portlet-content" id="bodyTotalTPS"> |
|||
<div class="panel-body" id="collapseTotalTPS"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotTotalTPS" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewTotalTPS" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerTotalTPS"> |
|||
<p id="legendTotalTPS" hidden></p> |
|||
<ul id="choicesTotalTPS" class="legend"></ul> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
|
|||
<div class="col-lg-12 portlet" id="responseTimeVsRequest"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"> </i> <span type="button" class="dropdown-toggle click-title span-title" data-toggle="collapse" href="#bodyResponseTimeVsRequest" aria-expanded="true" aria-controls="bodyResponseTimeVsRequest">Response Time Vs Request</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#responseTimeVsRequest" onClick="checkAll('choicesResponseTimeVsRequest');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#responseTimeVsRequest" onClick="uncheckAll('choicesResponseTimeVsRequest');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#responseTimeVsRequest" onclick="exportToPNG('flotResponseTimeVsRequest', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyResponseTimeVsRequest" aria-expanded="true" aria-controls="bodyResponseTimeVsRequest"> |
|||
<i class="fa fa-chevron-down"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse out portlet-content" id="bodyResponseTimeVsRequest"> |
|||
<div class="panel-body" id="collapseResponseTimeVsRequest"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotResponseTimeVsRequest" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewResponseTimeVsRequest" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerResponseRimeVsRequest"> |
|||
<p id="legendResponseTimeVsRequest" hidden></p> |
|||
<ul id="choicesResponseTimeVsRequest" class="legend"></ul> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
<div class="col-lg-12 portlet" id="latencyVsRequest"> |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading portlet-header"> |
|||
<i class="fa fa-bar-chart-o fa-fw"> </i> <span type="button" class="dropdown-toggle click-title span-title" data-toggle="collapse" href="#bodyLatenciesVsRequest" aria-expanded="true" aria-controls="bodyLatenciesVsRequest">Latency Vs Request</span> |
|||
<div class="pull-right"> |
|||
<div class="btn-group"> |
|||
<a class="btn btn-link btn-xs"> |
|||
<i class="glyphicon glyphicon-resize-vertical"></i> |
|||
</a> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-wrench"></i> |
|||
</button> |
|||
<ul class="dropdown-menu dropdown-user"> |
|||
<li><a href="#latencyVsRequest" onClick="checkAll('choicesLatencyVsRequest');">Display all samples</a> |
|||
</li> |
|||
<li><a href="#latencyVsRequest" onClick="uncheckAll('choicesLatencyVsRequest');">Hide all samples</a> |
|||
</li> |
|||
<li><a href="#latencyVsRequest" onclick="exportToPNG('flotLatenciesVsRequest', this);">Save as PNG</a></li> |
|||
</ul> |
|||
<button type="button" class="btn btn-link btn-xs dropdown-toggle" data-toggle="collapse" href="#bodyLatenciesVsRequest" aria-expanded="true" aria-controls="bodyLatenciesVsRequest"> |
|||
<i class="fa fa-chevron-down"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-heading --> |
|||
<div class="collapse out portlet-content" id="bodyLatenciesVsRequest"> |
|||
<div class="panel-body" id="collapseLatencyVsRequest"> |
|||
<div class="flot-chart"> |
|||
<div class="flot-chart-content" id="flotLatenciesVsRequest" style="float: left; width:80%;"></div> |
|||
<div style="float:left;margin-left:5px"> |
|||
<p>Zoom :</p> |
|||
<div id="overviewLatenciesVsRequest" style="width:190px;height:100px;"></div> |
|||
</div> |
|||
</div> |
|||
<div class="panel-footer" id="footerLatenciesVsRequest"> |
|||
<p id="legendLatencyVsRequest" hidden></p> |
|||
<ul id="choicesLatencyVsRequest" class="legend"></ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- /.panel-body --> |
|||
</div> |
|||
<!-- /.panel --> |
|||
</div> |
|||
|
|||
</div> |
|||
<!-- /.row --> |
|||
</div> |
|||
<!-- /#page-wrapper --> |
|||
|
|||
</div> |
|||
<!-- /#wrapper --> |
|||
|
|||
<!-- jQuery --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/jquery/dist/jquery.min.js"></script> |
|||
|
|||
<!-- Bootstrap Core JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> |
|||
|
|||
<!-- Metis Menu Plugin JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/metisMenu/dist/metisMenu.min.js"></script> |
|||
|
|||
<!-- Flot Charts JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/excanvas.min.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.pie.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.resize.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.canvas.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.time.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.selection.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot.tooltip/js/jquery.flot.tooltip.min.js"></script> |
|||
<script src="../../sbadmin2-1.0.7/bower_components/flot-axislabels/jquery.flot.axislabels.js"></script> |
|||
<script src="../js/hashtable.js"></script> |
|||
<script src="../js/jquery.numberformatter-1.2.3.min.js"></script> |
|||
<script src="../js/curvedLines.js"></script> |
|||
<script src="../js/dashboard-commons.js"></script> |
|||
<script src="../js/graph.js"></script> |
|||
<script src="../js/jquery-ui.min.js"></script> |
|||
<!-- Custom Theme JavaScript --> |
|||
<script src="../../sbadmin2-1.0.7/dist/js/sb-admin-2.js"></script> |
|||
<script src="../js/jquery.cookie.js"></script> |
|||
</body> |
|||
|
|||
</html> |
|||
|
After Width: | Height: | Size: 4.8 KiB |
@ -0,0 +1,203 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
|
|||
<meta charset="utf-8"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|||
<meta name="description" content=""> |
|||
<meta name="author" content=""> |
|||
|
|||
<title>Apache JMeter Dashboard</title> |
|||
|
|||
<!-- Bootstrap Core CSS --> |
|||
<link href="sbadmin2-1.0.7/bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"> |
|||
|
|||
<!-- MetisMenu CSS --> |
|||
<link href="sbadmin2-1.0.7/bower_components/metisMenu/dist/metisMenu.min.css" rel="stylesheet"> |
|||
|
|||
<!-- Custom CSS --> |
|||
<link href="sbadmin2-1.0.7/dist/css/sb-admin-2.css" rel="stylesheet"> |
|||
|
|||
<!-- Dashboard css --> |
|||
<link href="content/css/dashboard.css" rel="stylesheet"> |
|||
|
|||
<!-- Table sorter --> |
|||
<link href="content/css/theme.blue.css" rel="stylesheet"> |
|||
|
|||
<!-- icon --> |
|||
<link rel="icon" type="image/png" href="content/pages/icon-apache.png" /> |
|||
|
|||
<!-- Custom Fonts --> |
|||
<link href="sbadmin2-1.0.7/bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css"> |
|||
</head> |
|||
|
|||
<body> |
|||
<div id="wrapper"> |
|||
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0"> |
|||
<div class="navbar-header"> |
|||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> |
|||
<span class="sr-only">Toggle navigation</span> |
|||
<span class="icon-bar"></span> |
|||
<span class="icon-bar"></span> |
|||
<span class="icon-bar"></span> |
|||
</button> |
|||
<a class="navbar-brand" href="index.html">Apache JMeter Dashboard</a> |
|||
</div> |
|||
<div class="navbar-default sidebar" role="navigation"> |
|||
<div class="sidebar-nav navbar-collapse"> |
|||
<ul class="nav" id="side-menu"> |
|||
<li> |
|||
<a href="index.html"><i class="fa fa-dashboard fa-fw"></i> Dashboard</a> |
|||
</li> |
|||
<li> |
|||
<a href="#"><i class="fa fa-bar-chart-o fa-fw"></i> Charts<span class="fa arrow"></span></a> |
|||
<ul class="nav nav-second-level"> |
|||
<li> |
|||
<a href="content/pages/OverTime.html">Over Time</a> |
|||
</li> |
|||
<li> |
|||
<a href="content/pages/Throughput.html">Throughput</a> |
|||
</li> |
|||
<li> |
|||
<a href="content/pages/ResponseTimes.html">Response Times</a> |
|||
</li> |
|||
</ul> |
|||
<!-- /.nav-second-level --> |
|||
</li> |
|||
|
|||
<li> |
|||
<a href="#"><i class="fa fa-bar-chart-o fa-fw"></i> Customs Graphs<span class="fa arrow"></span></a> |
|||
<ul class="nav nav-second-level"> |
|||
<li> |
|||
<a href="content/pages/CustomsGraphs.html">Over Time</a> |
|||
</li> |
|||
</ul> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</nav> |
|||
<div id="page-wrapper"> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
<div class="panel panel-default" > |
|||
<div class="panel-heading" style="text-align:center;"> |
|||
<p class="dashboard-title">Test and Report information</p> |
|||
</div> |
|||
<div class="panel-body"> |
|||
<table id="generalInfos" class="table table-bordered table-condensed " > |
|||
<tr> |
|||
<td>Source file</td> |
|||
<td>"WithAbp.jtl"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Start Time</td> |
|||
<td>"12/31/23, 11:45 AM"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>End Time</td> |
|||
<td>"12/31/23, 11:46 AM"</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Filter for display</td> |
|||
<td>""</td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-lg-6"> |
|||
<div class="panel panel-default" style="text-align:center;"> |
|||
<div class="panel-heading"> |
|||
<p class="dashboard-title"><a href="https://en.wikipedia.org/wiki/Apdex" target="_blank">APDEX (Application Performance Index)</a></p> |
|||
</div> |
|||
<div class="panel-body"> |
|||
<section id="apdex" class="col-md-12 table-responsive"> |
|||
<table id="apdexTable" class="table table-bordered table-condensed tablesorter "> |
|||
</table> |
|||
</section> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-6" > |
|||
<div class="panel panel-default" style="text-align:center;"> |
|||
<div class="panel-heading"> |
|||
<p class="dashboard-title">Requests Summary</p> |
|||
</div> |
|||
<div class="panel-body"> |
|||
<div class="flot-chart" style="height:200px;"> |
|||
<div class="flot-chart-content" id="flot-requests-summary" style="height:200px;"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-12"> |
|||
<div class="panel panel-default" style="text-align:center;"> |
|||
<div class="panel-heading" > |
|||
<p class="dashboard-title">Statistics</p> |
|||
</div> |
|||
<div class="panel-body "> |
|||
<section class="col-md-12 table-responsive"> |
|||
<table id="statisticsTable" class="table table-bordered table-condensed tablesorter " > |
|||
</table> |
|||
</section> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-12"> |
|||
<div class="panel panel-default" style="text-align:center;"> |
|||
<div class="panel-heading"> |
|||
<p class="dashboard-title">Errors</p> |
|||
</div> |
|||
<div class="panel-body"> |
|||
<section class="col-md-12 table-responsive"> |
|||
<table id="errorsTable" class="table table-bordered table-condensed tablesorter "> |
|||
</table> |
|||
</section> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-12"> |
|||
<div class="panel panel-default" style="text-align:center;"> |
|||
<div class="panel-heading"> |
|||
<p class="dashboard-title">Top 5 Errors by sampler</p> |
|||
</div> |
|||
<div class="panel-body"> |
|||
<section class="col-md-12 table-responsive"> |
|||
<table id="top5ErrorsBySamplerTable" class="table table-bordered table-condensed tablesorter "> |
|||
</table> |
|||
</section> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- jQuery --> |
|||
<script src="sbadmin2-1.0.7/bower_components/jquery/dist/jquery.min.js"></script> |
|||
|
|||
<!-- Bootstrap Core JavaScript --> |
|||
<script src="sbadmin2-1.0.7/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> |
|||
<script src="sbadmin2-1.0.7/bower_components/flot/excanvas.min.js"></script> |
|||
<script src="sbadmin2-1.0.7/bower_components/flot/jquery.flot.js"></script> |
|||
<script src="sbadmin2-1.0.7/bower_components/flot/jquery.flot.pie.js"></script> |
|||
<script src="sbadmin2-1.0.7/bower_components/flot/jquery.flot.resize.js"></script> |
|||
<script src="sbadmin2-1.0.7/bower_components/flot/jquery.flot.time.js"></script> |
|||
<script src="sbadmin2-1.0.7/bower_components/flot.tooltip/js/jquery.flot.tooltip.min.js"></script> |
|||
<script src="sbadmin2-1.0.7/bower_components/flot-axislabels/jquery.flot.axislabels.js"></script> |
|||
<!-- Metis Menu Plugin JavaScript --> |
|||
<script src="sbadmin2-1.0.7/bower_components/metisMenu/dist/metisMenu.min.js"></script> |
|||
|
|||
<script src="content/js/dashboard-commons.js"></script> |
|||
<script src="content/js/dashboard.js"></script> |
|||
|
|||
<!-- Custom Theme JavaScript --> |
|||
<script src="sbadmin2-1.0.7/dist/js/sb-admin-2.js"></script> |
|||
<script type="text/javascript" src="content/js/jquery.tablesorter.min.js"></script> |
|||
|
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,27 @@ |
|||
# [Start Bootstrap](http://startbootstrap.com/) - [SB Admin 2](http://startbootstrap.com/template-overviews/sb-admin-2/) |
|||
|
|||
[SB Admin 2](http://startbootstrap.com/template-overviews/sb-admin-2/) is an open source, admin dashboard template for [Bootstrap](http://getbootstrap.com/) created by [Start Bootstrap](http://startbootstrap.com/). |
|||
|
|||
## Getting Started |
|||
|
|||
To use this template, choose one of the following options to get started: |
|||
* Download the latest release on Start Bootstrap |
|||
* Fork this repository on GitHub |
|||
* Install via bower using `bower install startbootstrap-sb-admin-2` |
|||
|
|||
## Bugs and Issues |
|||
|
|||
Have a bug or an issue with this template? [Open a new issue](https://github.com/IronSummitMedia/startbootstrap-sb-admin-2/issues) here on GitHub or leave a comment on the [template overview page at Start Bootstrap](http://startbootstrap.com/template-overviews/sb-admin-2/). |
|||
|
|||
## Creator |
|||
|
|||
Start Bootstrap was created by and is maintained by **David Miller**, Managing Parter at [Iron Summit Media Strategies](http://www.ironsummitmedia.com/). |
|||
|
|||
* https://twitter.com/davidmillerskt |
|||
* https://github.com/davidtmiller |
|||
|
|||
Start Bootstrap is based on the [Bootstrap](http://getbootstrap.com/) framework created by [Mark Otto](https://twitter.com/mdo) and [Jacob Thorton](https://twitter.com/fat). |
|||
|
|||
## Copyright and License |
|||
|
|||
Copyright 2013-2015 Iron Summit Media Strategies, LLC. Code released under the [Apache 2.0](https://github.com/IronSummitMedia/startbootstrap-sb-admin-2/blob/gh-pages/LICENSE) license. |
|||
@ -0,0 +1,39 @@ |
|||
{ |
|||
"name": "startbootstrap-sb-admin-2", |
|||
"version": "1.0.7", |
|||
"homepage": "http://startbootstrap.com/template-overviews/sb-admin-2/", |
|||
"authors": [ |
|||
"David Miller" |
|||
], |
|||
"description": "A free, open source, Bootstrap admin theme created by Start Bootstrap", |
|||
"keywords": [ |
|||
"bootstrap", |
|||
"theme" |
|||
], |
|||
"license": "Apache", |
|||
"ignore": [ |
|||
"**/.*", |
|||
"node_modules", |
|||
"bower_components", |
|||
"test", |
|||
"tests", |
|||
"pages", |
|||
"index.html", |
|||
"/js" |
|||
], |
|||
"main": [ |
|||
"dist/css/sb-admin-2.css", |
|||
"dist/js/sb-admin-2.js" |
|||
], |
|||
"dependencies": { |
|||
"bootstrap": "~3.3.1", |
|||
"datatables": "~1.10.4", |
|||
"datatables-plugins": "~1.0.1", |
|||
"flot": "~0.8.3", |
|||
"font-awesome": "~4.2.0", |
|||
"metisMenu": "~1.1.3", |
|||
"datatables-responsive": "~1.0.3", |
|||
"bootstrap-social": "~4.8.0", |
|||
"flot.tooltip": "~0.8.4" |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
{ |
|||
"name": "bootstrap", |
|||
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", |
|||
"version": "3.3.4", |
|||
"keywords": [ |
|||
"css", |
|||
"js", |
|||
"less", |
|||
"mobile-first", |
|||
"responsive", |
|||
"front-end", |
|||
"framework", |
|||
"web" |
|||
], |
|||
"homepage": "http://getbootstrap.com", |
|||
"main": [ |
|||
"less/bootstrap.less", |
|||
"dist/css/bootstrap.css", |
|||
"dist/js/bootstrap.js", |
|||
"dist/fonts/glyphicons-halflings-regular.eot", |
|||
"dist/fonts/glyphicons-halflings-regular.svg", |
|||
"dist/fonts/glyphicons-halflings-regular.ttf", |
|||
"dist/fonts/glyphicons-halflings-regular.woff", |
|||
"dist/fonts/glyphicons-halflings-regular.woff2" |
|||
], |
|||
"ignore": [ |
|||
"/.*", |
|||
"_config.yml", |
|||
"CNAME", |
|||
"composer.json", |
|||
"CONTRIBUTING.md", |
|||
"docs", |
|||
"js/tests", |
|||
"test-infra" |
|||
], |
|||
"dependencies": { |
|||
"jquery": ">= 1.9.1" |
|||
}, |
|||
"_release": "3.3.4", |
|||
"_resolution": { |
|||
"type": "version", |
|||
"tag": "v3.3.4", |
|||
"commit": "a10eb60bc0b07b747fa0c4ebd8821eb7307bd07f" |
|||
}, |
|||
"_source": "git://github.com/twbs/bootstrap.git", |
|||
"_target": "~3.3.1", |
|||
"_originalSource": "bootstrap" |
|||
} |
|||
@ -0,0 +1,134 @@ |
|||
# [Bootstrap](http://getbootstrap.com) |
|||
 |
|||
[](https://www.npmjs.com/package/bootstrap) |
|||
[](https://travis-ci.org/twbs/bootstrap) |
|||
[](https://david-dm.org/twbs/bootstrap#info=devDependencies) |
|||
[](https://saucelabs.com/u/bootstrap) |
|||
|
|||
Bootstrap is a sleek, intuitive, and powerful front-end framework for faster and easier web development, created by [Mark Otto](https://twitter.com/mdo) and [Jacob Thornton](https://twitter.com/fat), and maintained by the [core team](https://github.com/orgs/twbs/people) with the massive support and involvement of the community. |
|||
|
|||
To get started, check out <http://getbootstrap.com>! |
|||
|
|||
## Table of contents |
|||
|
|||
- [Quick start](#quick-start) |
|||
- [Bugs and feature requests](#bugs-and-feature-requests) |
|||
- [Documentation](#documentation) |
|||
- [Contributing](#contributing) |
|||
- [Community](#community) |
|||
- [Versioning](#versioning) |
|||
- [Creators](#creators) |
|||
- [Copyright and license](#copyright-and-license) |
|||
|
|||
## Quick start |
|||
|
|||
Five quick start options are available: |
|||
|
|||
- [Download the latest release](https://github.com/twbs/bootstrap/archive/v3.3.4.zip). |
|||
- Clone the repo: `git clone https://github.com/twbs/bootstrap.git`. |
|||
- Install with [Bower](http://bower.io): `bower install bootstrap`. |
|||
- Install with [npm](https://www.npmjs.com): `npm install bootstrap`. |
|||
- Install with [Meteor](https://www.meteor.com/): `meteor add twbs:bootstrap`. |
|||
|
|||
Read the [Getting started page](http://getbootstrap.com/getting-started/) for information on the framework contents, templates and examples, and more. |
|||
|
|||
### What's included |
|||
|
|||
Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations. You'll see something like this: |
|||
|
|||
``` |
|||
bootstrap/ |
|||
├── css/ |
|||
│ ├── bootstrap.css |
|||
│ ├── bootstrap.css.map |
|||
│ ├── bootstrap.min.css |
|||
│ ├── bootstrap-theme.css |
|||
│ ├── bootstrap-theme.css.map |
|||
│ └── bootstrap-theme.min.css |
|||
├── js/ |
|||
│ ├── bootstrap.js |
|||
│ └── bootstrap.min.js |
|||
└── fonts/ |
|||
├── glyphicons-halflings-regular.eot |
|||
├── glyphicons-halflings-regular.svg |
|||
├── glyphicons-halflings-regular.ttf |
|||
├── glyphicons-halflings-regular.woff |
|||
└── glyphicons-halflings-regular.woff2 |
|||
``` |
|||
|
|||
We provide compiled CSS and JS (`bootstrap.*`), as well as compiled and minified CSS and JS (`bootstrap.min.*`). CSS [source maps](https://developers.google.com/chrome-developer-tools/docs/css-preprocessors) (`bootstrap.*.map`) are available for use with certain browsers' developer tools. Fonts from Glyphicons are included, as is the optional Bootstrap theme. |
|||
|
|||
|
|||
|
|||
## Bugs and feature requests |
|||
|
|||
Have a bug or a feature request? Please first read the [issue guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/twbs/bootstrap/issues/new). |
|||
|
|||
|
|||
## Documentation |
|||
|
|||
Bootstrap's documentation, included in this repo in the root directory, is built with [Jekyll](http://jekyllrb.com) and publicly hosted on GitHub Pages at <http://getbootstrap.com>. The docs may also be run locally. |
|||
|
|||
### Running documentation locally |
|||
|
|||
1. If necessary, [install Jekyll](http://jekyllrb.com/docs/installation) (requires v2.5.x). |
|||
- **Windows users:** Read [this unofficial guide](http://jekyll-windows.juthilo.com/) to get Jekyll up and running without problems. |
|||
2. Install the Ruby-based syntax highlighter, [Rouge](https://github.com/jneen/rouge), with `gem install rouge`. |
|||
3. From the root `/bootstrap` directory, run `jekyll serve` in the command line. |
|||
4. Open <http://localhost:9001> in your browser, and voilà. |
|||
|
|||
Learn more about using Jekyll by reading its [documentation](http://jekyllrb.com/docs/home/). |
|||
|
|||
### Documentation for previous releases |
|||
|
|||
Documentation for v2.3.2 has been made available for the time being at <http://getbootstrap.com/2.3.2/> while folks transition to Bootstrap 3. |
|||
|
|||
[Previous releases](https://github.com/twbs/bootstrap/releases) and their documentation are also available for download. |
|||
|
|||
|
|||
|
|||
## Contributing |
|||
|
|||
Please read through our [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development. |
|||
|
|||
Moreover, if your pull request contains JavaScript patches or features, you must include [relevant unit tests](https://github.com/twbs/bootstrap/tree/master/js/tests). All HTML and CSS should conform to the [Code Guide](https://github.com/mdo/code-guide), maintained by [Mark Otto](https://github.com/mdo). |
|||
|
|||
Editor preferences are available in the [editor config](https://github.com/twbs/bootstrap/blob/master/.editorconfig) for easy use in common text editors. Read more and download plugins at <http://editorconfig.org>. |
|||
|
|||
|
|||
|
|||
## Community |
|||
|
|||
Keep track of development and community news. |
|||
|
|||
- Follow [@getbootstrap on Twitter](https://twitter.com/getbootstrap). |
|||
- Read and subscribe to [The Official Bootstrap Blog](http://blog.getbootstrap.com). |
|||
- Chat with fellow Bootstrappers in IRC. On the `irc.freenode.net` server, in the `##bootstrap` channel. |
|||
- Implementation help may be found at Stack Overflow (tagged [`twitter-bootstrap-3`](http://stackoverflow.com/questions/tagged/twitter-bootstrap-3)). |
|||
- Developers should use the keyword `bootstrap` on packages which modify or add to the functionality of Bootstrap when distributing through [npm](https://www.npmjs.com/browse/keyword/bootstrap) or similar delivery mechanisms for maximum discoverability. |
|||
|
|||
|
|||
|
|||
## Versioning |
|||
|
|||
For transparency into our release cycle and in striving to maintain backward compatibility, Bootstrap is maintained under [the Semantic Versioning guidelines](http://semver.org/). Sometimes we screw up, but we'll adhere to those rules whenever possible. |
|||
|
|||
|
|||
|
|||
## Creators |
|||
|
|||
**Mark Otto** |
|||
|
|||
- <https://twitter.com/mdo> |
|||
- <https://github.com/mdo> |
|||
|
|||
**Jacob Thornton** |
|||
|
|||
- <https://twitter.com/fat> |
|||
- <https://github.com/fat> |
|||
|
|||
|
|||
|
|||
## Copyright and license |
|||
|
|||
Code and documentation copyright 2011-2015 Twitter, Inc. Code released under [the MIT license](https://github.com/twbs/bootstrap/blob/master/LICENSE). Docs released under [Creative Commons](https://github.com/twbs/bootstrap/blob/master/docs/LICENSE). |
|||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -0,0 +1,14 @@ |
|||
{ |
|||
"name": "flot-axislabels", |
|||
"homepage": "https://github.com/markrcote/flot-axislabels", |
|||
"_release": "a181e09d04", |
|||
"_resolution": { |
|||
"type": "branch", |
|||
"branch": "master", |
|||
"commit": "a181e09d04d120d05e5bc2baaa8738b5b3670428" |
|||
}, |
|||
"_source": "git://github.com/markrcote/flot-axislabels.git", |
|||
"_target": "*", |
|||
"_originalSource": "flot-axislabels", |
|||
"_direct": true |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
flot-axislabels: Axis Labels plugin for flot |
|||
============================================ |
|||
|
|||
Originally written by Xuan Luo. Maintained by Mark Cote. |
|||
|
|||
Contributions: |
|||
|
|||
* Xuan Luo |
|||
* Mark Cote |
|||
* stdexcept |
|||
* Clemens Stolle |
|||
* Michael Haddon |
|||
* andig |
|||
* Alex Pinkney |
|||
|
|||
[flot-axislabels](https://github.com/markrcote/flot-axislabels) provides |
|||
flot with the ability to label axes. It supports any number of axes. It |
|||
can render the labels with CSS transforms, in canvas, or with traditional |
|||
CSS positioning ("HTML" mode). flot-axislabels attempts a graceful fallback |
|||
from CSS to canvas to HTML if some modes are not supported. You can also |
|||
force a particular lesser mode (canvas or HTML). |
|||
|
|||
In both CSS and canvas modes, the y-axis labels are rotated to face the |
|||
graph (90 degrees counter-clockwise for left-hand labels, and 90 degrees |
|||
clockwise for right-hand labels). In HTML mode, y-axis labels are left |
|||
horizontal (warning: this takes up a lot of space). |
|||
|
|||
In CSS and HTML modes, each axis label belongs to the classes "axisLabels" |
|||
and "[axisName]Label" (e.g. .xaxisLabel, .y2axisLabel, etc). You can use |
|||
standard CSS properties to customize their appearance. |
|||
|
|||
In canvas mode, you can set font size, family, and colour through flot |
|||
options (see below). |
|||
|
|||
|
|||
Example |
|||
------- |
|||
|
|||
$(function () { |
|||
var options = { |
|||
axisLabels: { |
|||
show: true |
|||
}, |
|||
xaxes: [{ |
|||
axisLabel: 'foo', |
|||
}], |
|||
yaxes: [{ |
|||
position: 'left', |
|||
axisLabel: 'bar', |
|||
}, { |
|||
position: 'right', |
|||
axisLabel: 'bleem' |
|||
}] |
|||
}; |
|||
|
|||
$.plot($("#placeholder"), |
|||
yourData, |
|||
options); |
|||
); |
|||
}); |
|||
|
|||
|
|||
Usage |
|||
----- |
|||
|
|||
flot-axislabel adds an axisLabels object to the global options object. |
|||
It supports one option: |
|||
|
|||
* show (bool): display all axis labels (default: true) |
|||
|
|||
There are also several options added to the axis objects. The two main ones |
|||
are |
|||
|
|||
* axisLabel (string): the text you want displayed as the label |
|||
* axisLabelPadding (int): padding, in pixels, between the tick labels and the |
|||
axis label (default: 2) |
|||
|
|||
By default, if supported, flot-axislabels uses CSS transforms. You can force |
|||
either canvas or HTML mode by setting axisLabelUseCanvas or axisLabelUseHtml, |
|||
respectively, to true. |
|||
|
|||
Canvas mode supports several other options: |
|||
|
|||
* axisLabelFontSizePixels (int): the size, in pixels, of the font (default: 14) |
|||
* axisLabelFontFamily (string): the font family of the font (default: |
|||
sans-serif) |
|||
* axisLabelColour (string): the font colour (default: black) |
|||
|
|||
|
|||
Compatibility |
|||
------------- |
|||
|
|||
flot-axislabels should work with recent versions of Firefox, Chrome, Opera, |
|||
and Safari. It also works with IE 8 and 9. The canvas option does *not* |
|||
seem to work with IE 8, even with excanvas. |
|||
|
|||
|
|||
License |
|||
------- |
|||
|
|||
flot-axislabels is released under the terms of [the MIT License](http://www.opensource.org/licenses/MIT). |
|||
@ -0,0 +1,466 @@ |
|||
/* |
|||
Axis Labels Plugin for flot. |
|||
http://github.com/markrcote/flot-axislabels
|
|||
|
|||
Original code is Copyright (c) 2010 Xuan Luo. |
|||
Original code was released under the GPLv3 license by Xuan Luo, September 2010. |
|||
Original code was rereleased under the MIT license by Xuan Luo, April 2012. |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining |
|||
a copy of this software and associated documentation files (the |
|||
"Software"), to deal in the Software without restriction, including |
|||
without limitation the rights to use, copy, modify, merge, publish, |
|||
distribute, sublicense, and/or sell copies of the Software, and to |
|||
permit persons to whom the Software is furnished to do so, subject to |
|||
the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be |
|||
included in all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|||
*/ |
|||
|
|||
(function ($) { |
|||
var options = { |
|||
axisLabels: { |
|||
show: true |
|||
} |
|||
}; |
|||
|
|||
function canvasSupported() { |
|||
return !!document.createElement('canvas').getContext; |
|||
} |
|||
|
|||
function canvasTextSupported() { |
|||
if (!canvasSupported()) { |
|||
return false; |
|||
} |
|||
var dummy_canvas = document.createElement('canvas'); |
|||
var context = dummy_canvas.getContext('2d'); |
|||
return typeof context.fillText == 'function'; |
|||
} |
|||
|
|||
function css3TransitionSupported() { |
|||
var div = document.createElement('div'); |
|||
return typeof div.style.MozTransition != 'undefined' // Gecko
|
|||
|| typeof div.style.OTransition != 'undefined' // Opera
|
|||
|| typeof div.style.webkitTransition != 'undefined' // WebKit
|
|||
|| typeof div.style.transition != 'undefined'; |
|||
} |
|||
|
|||
|
|||
function AxisLabel(axisName, position, padding, plot, opts) { |
|||
this.axisName = axisName; |
|||
this.position = position; |
|||
this.padding = padding; |
|||
this.plot = plot; |
|||
this.opts = opts; |
|||
this.width = 0; |
|||
this.height = 0; |
|||
} |
|||
|
|||
AxisLabel.prototype.cleanup = function() { |
|||
}; |
|||
|
|||
|
|||
CanvasAxisLabel.prototype = new AxisLabel(); |
|||
CanvasAxisLabel.prototype.constructor = CanvasAxisLabel; |
|||
function CanvasAxisLabel(axisName, position, padding, plot, opts) { |
|||
AxisLabel.prototype.constructor.call(this, axisName, position, padding, |
|||
plot, opts); |
|||
} |
|||
|
|||
CanvasAxisLabel.prototype.calculateSize = function() { |
|||
if (!this.opts.axisLabelFontSizePixels) |
|||
this.opts.axisLabelFontSizePixels = 14; |
|||
if (!this.opts.axisLabelFontFamily) |
|||
this.opts.axisLabelFontFamily = 'sans-serif'; |
|||
|
|||
var textWidth = this.opts.axisLabelFontSizePixels + this.padding; |
|||
var textHeight = this.opts.axisLabelFontSizePixels + this.padding; |
|||
if (this.position == 'left' || this.position == 'right') { |
|||
this.width = this.opts.axisLabelFontSizePixels + this.padding; |
|||
this.height = 0; |
|||
} else { |
|||
this.width = 0; |
|||
this.height = this.opts.axisLabelFontSizePixels + this.padding; |
|||
} |
|||
}; |
|||
|
|||
CanvasAxisLabel.prototype.draw = function(box) { |
|||
if (!this.opts.axisLabelColour) |
|||
this.opts.axisLabelColour = 'black'; |
|||
var ctx = this.plot.getCanvas().getContext('2d'); |
|||
ctx.save(); |
|||
ctx.font = this.opts.axisLabelFontSizePixels + 'px ' + |
|||
this.opts.axisLabelFontFamily; |
|||
ctx.fillStyle = this.opts.axisLabelColour; |
|||
var width = ctx.measureText(this.opts.axisLabel).width; |
|||
var height = this.opts.axisLabelFontSizePixels; |
|||
var x, y, angle = 0; |
|||
if (this.position == 'top') { |
|||
x = box.left + box.width/2 - width/2; |
|||
y = box.top + height*0.72; |
|||
} else if (this.position == 'bottom') { |
|||
x = box.left + box.width/2 - width/2; |
|||
y = box.top + box.height - height*0.72; |
|||
} else if (this.position == 'left') { |
|||
x = box.left + height*0.72; |
|||
y = box.height/2 + box.top + width/2; |
|||
angle = -Math.PI/2; |
|||
} else if (this.position == 'right') { |
|||
x = box.left + box.width - height*0.72; |
|||
y = box.height/2 + box.top - width/2; |
|||
angle = Math.PI/2; |
|||
} |
|||
ctx.translate(x, y); |
|||
ctx.rotate(angle); |
|||
ctx.fillText(this.opts.axisLabel, 0, 0); |
|||
ctx.restore(); |
|||
}; |
|||
|
|||
|
|||
HtmlAxisLabel.prototype = new AxisLabel(); |
|||
HtmlAxisLabel.prototype.constructor = HtmlAxisLabel; |
|||
function HtmlAxisLabel(axisName, position, padding, plot, opts) { |
|||
AxisLabel.prototype.constructor.call(this, axisName, position, |
|||
padding, plot, opts); |
|||
this.elem = null; |
|||
} |
|||
|
|||
HtmlAxisLabel.prototype.calculateSize = function() { |
|||
var elem = $('<div class="axisLabels" style="position:absolute;">' + |
|||
this.opts.axisLabel + '</div>'); |
|||
this.plot.getPlaceholder().append(elem); |
|||
// store height and width of label itself, for use in draw()
|
|||
this.labelWidth = elem.outerWidth(true); |
|||
this.labelHeight = elem.outerHeight(true); |
|||
elem.remove(); |
|||
|
|||
this.width = this.height = 0; |
|||
if (this.position == 'left' || this.position == 'right') { |
|||
this.width = this.labelWidth + this.padding; |
|||
} else { |
|||
this.height = this.labelHeight + this.padding; |
|||
} |
|||
}; |
|||
|
|||
HtmlAxisLabel.prototype.cleanup = function() { |
|||
if (this.elem) { |
|||
this.elem.remove(); |
|||
} |
|||
}; |
|||
|
|||
HtmlAxisLabel.prototype.draw = function(box) { |
|||
this.plot.getPlaceholder().find('#' + this.axisName + 'Label').remove(); |
|||
this.elem = $('<div id="' + this.axisName + |
|||
'Label" " class="axisLabels" style="position:absolute;">' |
|||
+ this.opts.axisLabel + '</div>'); |
|||
this.plot.getPlaceholder().append(this.elem); |
|||
if (this.position == 'top') { |
|||
this.elem.css('left', box.left + box.width/2 - this.labelWidth/2 + |
|||
'px'); |
|||
this.elem.css('top', box.top + 'px'); |
|||
} else if (this.position == 'bottom') { |
|||
this.elem.css('left', box.left + box.width/2 - this.labelWidth/2 + |
|||
'px'); |
|||
this.elem.css('top', box.top + box.height - this.labelHeight + |
|||
'px'); |
|||
} else if (this.position == 'left') { |
|||
this.elem.css('top', box.top + box.height/2 - this.labelHeight/2 + |
|||
'px'); |
|||
this.elem.css('left', box.left + 'px'); |
|||
} else if (this.position == 'right') { |
|||
this.elem.css('top', box.top + box.height/2 - this.labelHeight/2 + |
|||
'px'); |
|||
this.elem.css('left', box.left + box.width - this.labelWidth + |
|||
'px'); |
|||
} |
|||
}; |
|||
|
|||
|
|||
CssTransformAxisLabel.prototype = new HtmlAxisLabel(); |
|||
CssTransformAxisLabel.prototype.constructor = CssTransformAxisLabel; |
|||
function CssTransformAxisLabel(axisName, position, padding, plot, opts) { |
|||
HtmlAxisLabel.prototype.constructor.call(this, axisName, position, |
|||
padding, plot, opts); |
|||
} |
|||
|
|||
CssTransformAxisLabel.prototype.calculateSize = function() { |
|||
HtmlAxisLabel.prototype.calculateSize.call(this); |
|||
this.width = this.height = 0; |
|||
if (this.position == 'left' || this.position == 'right') { |
|||
this.width = this.labelHeight + this.padding; |
|||
} else { |
|||
this.height = this.labelHeight + this.padding; |
|||
} |
|||
}; |
|||
|
|||
CssTransformAxisLabel.prototype.transforms = function(degrees, x, y) { |
|||
var stransforms = { |
|||
'-moz-transform': '', |
|||
'-webkit-transform': '', |
|||
'-o-transform': '', |
|||
'-ms-transform': '' |
|||
}; |
|||
if (x != 0 || y != 0) { |
|||
var stdTranslate = ' translate(' + x + 'px, ' + y + 'px)'; |
|||
stransforms['-moz-transform'] += stdTranslate; |
|||
stransforms['-webkit-transform'] += stdTranslate; |
|||
stransforms['-o-transform'] += stdTranslate; |
|||
stransforms['-ms-transform'] += stdTranslate; |
|||
} |
|||
if (degrees != 0) { |
|||
var rotation = degrees / 90; |
|||
var stdRotate = ' rotate(' + degrees + 'deg)'; |
|||
stransforms['-moz-transform'] += stdRotate; |
|||
stransforms['-webkit-transform'] += stdRotate; |
|||
stransforms['-o-transform'] += stdRotate; |
|||
stransforms['-ms-transform'] += stdRotate; |
|||
} |
|||
var s = 'top: 0; left: 0; '; |
|||
for (var prop in stransforms) { |
|||
if (stransforms[prop]) { |
|||
s += prop + ':' + stransforms[prop] + ';'; |
|||
} |
|||
} |
|||
s += ';'; |
|||
return s; |
|||
}; |
|||
|
|||
CssTransformAxisLabel.prototype.calculateOffsets = function(box) { |
|||
var offsets = { x: 0, y: 0, degrees: 0 }; |
|||
if (this.position == 'bottom') { |
|||
offsets.x = box.left + box.width/2 - this.labelWidth/2; |
|||
offsets.y = box.top + box.height - this.labelHeight; |
|||
} else if (this.position == 'top') { |
|||
offsets.x = box.left + box.width/2 - this.labelWidth/2; |
|||
offsets.y = box.top; |
|||
} else if (this.position == 'left') { |
|||
offsets.degrees = -90; |
|||
offsets.x = box.left - this.labelWidth/2 + this.labelHeight/2; |
|||
offsets.y = box.height/2 + box.top; |
|||
} else if (this.position == 'right') { |
|||
offsets.degrees = 90; |
|||
offsets.x = box.left + box.width - this.labelWidth/2 |
|||
- this.labelHeight/2; |
|||
offsets.y = box.height/2 + box.top; |
|||
} |
|||
offsets.x = Math.round(offsets.x); |
|||
offsets.y = Math.round(offsets.y); |
|||
|
|||
return offsets; |
|||
}; |
|||
|
|||
CssTransformAxisLabel.prototype.draw = function(box) { |
|||
this.plot.getPlaceholder().find("." + this.axisName + "Label").remove(); |
|||
var offsets = this.calculateOffsets(box); |
|||
this.elem = $('<div class="axisLabels ' + this.axisName + |
|||
'Label" style="position:absolute; ' + |
|||
this.transforms(offsets.degrees, offsets.x, offsets.y) + |
|||
'">' + this.opts.axisLabel + '</div>'); |
|||
this.plot.getPlaceholder().append(this.elem); |
|||
}; |
|||
|
|||
|
|||
IeTransformAxisLabel.prototype = new CssTransformAxisLabel(); |
|||
IeTransformAxisLabel.prototype.constructor = IeTransformAxisLabel; |
|||
function IeTransformAxisLabel(axisName, position, padding, plot, opts) { |
|||
CssTransformAxisLabel.prototype.constructor.call(this, axisName, |
|||
position, padding, |
|||
plot, opts); |
|||
this.requiresResize = false; |
|||
} |
|||
|
|||
IeTransformAxisLabel.prototype.transforms = function(degrees, x, y) { |
|||
// I didn't feel like learning the crazy Matrix stuff, so this uses
|
|||
// a combination of the rotation transform and CSS positioning.
|
|||
var s = ''; |
|||
if (degrees != 0) { |
|||
var rotation = degrees/90; |
|||
while (rotation < 0) { |
|||
rotation += 4; |
|||
} |
|||
s += ' filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=' + rotation + '); '; |
|||
// see below
|
|||
this.requiresResize = (this.position == 'right'); |
|||
} |
|||
if (x != 0) { |
|||
s += 'left: ' + x + 'px; '; |
|||
} |
|||
if (y != 0) { |
|||
s += 'top: ' + y + 'px; '; |
|||
} |
|||
return s; |
|||
}; |
|||
|
|||
IeTransformAxisLabel.prototype.calculateOffsets = function(box) { |
|||
var offsets = CssTransformAxisLabel.prototype.calculateOffsets.call( |
|||
this, box); |
|||
// adjust some values to take into account differences between
|
|||
// CSS and IE rotations.
|
|||
if (this.position == 'top') { |
|||
// FIXME: not sure why, but placing this exactly at the top causes
|
|||
// the top axis label to flip to the bottom...
|
|||
offsets.y = box.top + 1; |
|||
} else if (this.position == 'left') { |
|||
offsets.x = box.left; |
|||
offsets.y = box.height/2 + box.top - this.labelWidth/2; |
|||
} else if (this.position == 'right') { |
|||
offsets.x = box.left + box.width - this.labelHeight; |
|||
offsets.y = box.height/2 + box.top - this.labelWidth/2; |
|||
} |
|||
return offsets; |
|||
}; |
|||
|
|||
IeTransformAxisLabel.prototype.draw = function(box) { |
|||
CssTransformAxisLabel.prototype.draw.call(this, box); |
|||
if (this.requiresResize) { |
|||
this.elem = this.plot.getPlaceholder().find("." + this.axisName + |
|||
"Label"); |
|||
// Since we used CSS positioning instead of transforms for
|
|||
// translating the element, and since the positioning is done
|
|||
// before any rotations, we have to reset the width and height
|
|||
// in case the browser wrapped the text (specifically for the
|
|||
// y2axis).
|
|||
this.elem.css('width', this.labelWidth); |
|||
this.elem.css('height', this.labelHeight); |
|||
} |
|||
}; |
|||
|
|||
|
|||
function init(plot) { |
|||
plot.hooks.processOptions.push(function (plot, options) { |
|||
|
|||
if (!options.axisLabels.show) |
|||
return; |
|||
|
|||
// This is kind of a hack. There are no hooks in Flot between
|
|||
// the creation and measuring of the ticks (setTicks, measureTickLabels
|
|||
// in setupGrid() ) and the drawing of the ticks and plot box
|
|||
// (insertAxisLabels in setupGrid() ).
|
|||
//
|
|||
// Therefore, we use a trick where we run the draw routine twice:
|
|||
// the first time to get the tick measurements, so that we can change
|
|||
// them, and then have it draw it again.
|
|||
var secondPass = false; |
|||
|
|||
var axisLabels = {}; |
|||
var axisOffsetCounts = { left: 0, right: 0, top: 0, bottom: 0 }; |
|||
|
|||
var defaultPadding = 2; // padding between axis and tick labels
|
|||
plot.hooks.draw.push(function (plot, ctx) { |
|||
var hasAxisLabels = false; |
|||
if (!secondPass) { |
|||
// MEASURE AND SET OPTIONS
|
|||
$.each(plot.getAxes(), function(axisName, axis) { |
|||
var opts = axis.options // Flot 0.7
|
|||
|| plot.getOptions()[axisName]; // Flot 0.6
|
|||
|
|||
// Handle redraws initiated outside of this plug-in.
|
|||
if (axisName in axisLabels) { |
|||
axis.labelHeight = axis.labelHeight - |
|||
axisLabels[axisName].height; |
|||
axis.labelWidth = axis.labelWidth - |
|||
axisLabels[axisName].width; |
|||
opts.labelHeight = axis.labelHeight; |
|||
opts.labelWidth = axis.labelWidth; |
|||
axisLabels[axisName].cleanup(); |
|||
delete axisLabels[axisName]; |
|||
} |
|||
|
|||
if (!opts || !opts.axisLabel || !axis.show) |
|||
return; |
|||
|
|||
hasAxisLabels = true; |
|||
var renderer = null; |
|||
|
|||
if (!opts.axisLabelUseHtml && |
|||
navigator.appName == 'Microsoft Internet Explorer') { |
|||
var ua = navigator.userAgent; |
|||
var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); |
|||
if (re.exec(ua) != null) { |
|||
rv = parseFloat(RegExp.$1); |
|||
} |
|||
if (rv >= 9 && !opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) { |
|||
renderer = CssTransformAxisLabel; |
|||
} else if (!opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) { |
|||
renderer = IeTransformAxisLabel; |
|||
} else if (opts.axisLabelUseCanvas) { |
|||
renderer = CanvasAxisLabel; |
|||
} else { |
|||
renderer = HtmlAxisLabel; |
|||
} |
|||
} else { |
|||
if (opts.axisLabelUseHtml || (!css3TransitionSupported() && !canvasTextSupported()) && !opts.axisLabelUseCanvas) { |
|||
renderer = HtmlAxisLabel; |
|||
} else if (opts.axisLabelUseCanvas || !css3TransitionSupported()) { |
|||
renderer = CanvasAxisLabel; |
|||
} else { |
|||
renderer = CssTransformAxisLabel; |
|||
} |
|||
} |
|||
|
|||
var padding = opts.axisLabelPadding === undefined ? |
|||
defaultPadding : opts.axisLabelPadding; |
|||
|
|||
axisLabels[axisName] = new renderer(axisName, |
|||
axis.position, padding, |
|||
plot, opts); |
|||
|
|||
// flot interprets axis.labelHeight and .labelWidth as
|
|||
// the height and width of the tick labels. We increase
|
|||
// these values to make room for the axis label and
|
|||
// padding.
|
|||
|
|||
axisLabels[axisName].calculateSize(); |
|||
|
|||
// AxisLabel.height and .width are the size of the
|
|||
// axis label and padding.
|
|||
// Just set opts here because axis will be sorted out on
|
|||
// the redraw.
|
|||
|
|||
opts.labelHeight = axis.labelHeight + |
|||
axisLabels[axisName].height; |
|||
opts.labelWidth = axis.labelWidth + |
|||
axisLabels[axisName].width; |
|||
}); |
|||
|
|||
// If there are axis labels, re-draw with new label widths and
|
|||
// heights.
|
|||
|
|||
if (hasAxisLabels) { |
|||
secondPass = true; |
|||
plot.setupGrid(); |
|||
plot.draw(); |
|||
} |
|||
} else { |
|||
secondPass = false; |
|||
// DRAW
|
|||
$.each(plot.getAxes(), function(axisName, axis) { |
|||
var opts = axis.options // Flot 0.7
|
|||
|| plot.getOptions()[axisName]; // Flot 0.6
|
|||
if (!opts || !opts.axisLabel || !axis.show) |
|||
return; |
|||
|
|||
axisLabels[axisName].draw(axis.box); |
|||
}); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
|
|||
$.plot.plugins.push({ |
|||
init: init, |
|||
options: options, |
|||
name: 'axisLabels', |
|||
version: '2.0' |
|||
}); |
|||
})(jQuery); |
|||
@ -0,0 +1,24 @@ |
|||
{ |
|||
"name": "flot.tooltip", |
|||
"version": "0.8.4", |
|||
"main": "js/jquery.flot.tooltip.js", |
|||
"ignore": [ |
|||
".gitignore", |
|||
"Gruntfile.js", |
|||
"README.md", |
|||
"package.json", |
|||
"examples", |
|||
"js/old" |
|||
], |
|||
"homepage": "https://github.com/krzysu/flot.tooltip", |
|||
"_release": "0.8.4", |
|||
"_resolution": { |
|||
"type": "version", |
|||
"tag": "0.8.4", |
|||
"commit": "312a7570f6dfeff00d9c0ce6f6263160b84ee652" |
|||
}, |
|||
"_source": "git://github.com/krzysu/flot.tooltip.git", |
|||
"_target": "~0.8.4", |
|||
"_originalSource": "flot.tooltip", |
|||
"_direct": true |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
{ |
|||
"name": "flot.tooltip", |
|||
"version": "0.7.1", |
|||
"main": "js/jquery.flot.tooltip.js", |
|||
"ignore": [ |
|||
".gitignore", |
|||
"Gruntfile.js", |
|||
"README.md", |
|||
"package.json", |
|||
"examples", |
|||
"js/old" |
|||
] |
|||
} |
|||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
@ -0,0 +1,490 @@ |
|||
/* |
|||
* jquery.flot.tooltip |
|||
* |
|||
* description: easy-to-use tooltips for Flot charts |
|||
* version: 0.8.4 |
|||
* authors: Krzysztof Urbas @krzysu [myviews.pl],Evan Steinkerchner @Roundaround |
|||
* website: https://github.com/krzysu/flot.tooltip
|
|||
* |
|||
* build on 2014-08-04 |
|||
* released under MIT License, 2012 |
|||
*/ |
|||
(function ($) { |
|||
// plugin options, default values
|
|||
var defaultOptions = { |
|||
tooltip: false, |
|||
tooltipOpts: { |
|||
id: "flotTip", |
|||
content: "%s | X: %x | Y: %y", |
|||
// allowed templates are:
|
|||
// %s -> series label,
|
|||
// %lx -> x axis label (requires flot-axislabels plugin https://github.com/markrcote/flot-axislabels),
|
|||
// %ly -> y axis label (requires flot-axislabels plugin https://github.com/markrcote/flot-axislabels),
|
|||
// %x -> X value,
|
|||
// %y -> Y value,
|
|||
// %x.2 -> precision of X value,
|
|||
// %p -> percent
|
|||
xDateFormat: null, |
|||
yDateFormat: null, |
|||
monthNames: null, |
|||
dayNames: null, |
|||
shifts: { |
|||
x: 10, |
|||
y: 20 |
|||
}, |
|||
defaultTheme: true, |
|||
lines: { |
|||
track: false, |
|||
threshold: 0.05 |
|||
}, |
|||
|
|||
// callbacks
|
|||
onHover: function (flotItem, $tooltipEl) {}, |
|||
|
|||
$compat: false |
|||
} |
|||
}; |
|||
|
|||
// object
|
|||
var FlotTooltip = function (plot) { |
|||
// variables
|
|||
this.tipPosition = {x: 0, y: 0}; |
|||
|
|||
this.init(plot); |
|||
}; |
|||
|
|||
// main plugin function
|
|||
FlotTooltip.prototype.init = function (plot) { |
|||
var that = this; |
|||
|
|||
// detect other flot plugins
|
|||
var plotPluginsLength = $.plot.plugins.length; |
|||
this.plotPlugins = []; |
|||
|
|||
if (plotPluginsLength) { |
|||
for (var p = 0; p < plotPluginsLength; p++) { |
|||
this.plotPlugins.push($.plot.plugins[p].name); |
|||
} |
|||
} |
|||
|
|||
plot.hooks.bindEvents.push(function (plot, eventHolder) { |
|||
|
|||
// get plot options
|
|||
that.plotOptions = plot.getOptions(); |
|||
|
|||
// if not enabled return
|
|||
if (that.plotOptions.tooltip === false || typeof that.plotOptions.tooltip === 'undefined') return; |
|||
|
|||
// shortcut to access tooltip options
|
|||
that.tooltipOptions = that.plotOptions.tooltipOpts; |
|||
|
|||
if (that.tooltipOptions.$compat) { |
|||
that.wfunc = 'width'; |
|||
that.hfunc = 'height'; |
|||
} else { |
|||
that.wfunc = 'innerWidth'; |
|||
that.hfunc = 'innerHeight'; |
|||
} |
|||
|
|||
// create tooltip DOM element
|
|||
var $tip = that.getDomElement(); |
|||
|
|||
// bind event
|
|||
$( plot.getPlaceholder() ).bind("plothover", plothover); |
|||
|
|||
$(eventHolder).bind('mousemove', mouseMove); |
|||
}); |
|||
|
|||
plot.hooks.shutdown.push(function (plot, eventHolder){ |
|||
$(plot.getPlaceholder()).unbind("plothover", plothover); |
|||
$(eventHolder).unbind("mousemove", mouseMove); |
|||
}); |
|||
|
|||
function mouseMove(e){ |
|||
var pos = {}; |
|||
pos.x = e.pageX; |
|||
pos.y = e.pageY; |
|||
plot.setTooltipPosition(pos); |
|||
} |
|||
|
|||
function plothover(event, pos, item) { |
|||
// Simple distance formula.
|
|||
var lineDistance = function (p1x, p1y, p2x, p2y) { |
|||
return Math.sqrt((p2x - p1x) * (p2x - p1x) + (p2y - p1y) * (p2y - p1y)); |
|||
}; |
|||
|
|||
// Here is some voodoo magic for determining the distance to a line form a given point {x, y}.
|
|||
var dotLineLength = function (x, y, x0, y0, x1, y1, o) { |
|||
if (o && !(o = |
|||
function (x, y, x0, y0, x1, y1) { |
|||
if (typeof x0 !== 'undefined') return { x: x0, y: y }; |
|||
else if (typeof y0 !== 'undefined') return { x: x, y: y0 }; |
|||
|
|||
var left, |
|||
tg = -1 / ((y1 - y0) / (x1 - x0)); |
|||
|
|||
return { |
|||
x: left = (x1 * (x * tg - y + y0) + x0 * (x * -tg + y - y1)) / (tg * (x1 - x0) + y0 - y1), |
|||
y: tg * left - tg * x + y |
|||
}; |
|||
} (x, y, x0, y0, x1, y1), |
|||
o.x >= Math.min(x0, x1) && o.x <= Math.max(x0, x1) && o.y >= Math.min(y0, y1) && o.y <= Math.max(y0, y1)) |
|||
) { |
|||
var l1 = lineDistance(x, y, x0, y0), l2 = lineDistance(x, y, x1, y1); |
|||
return l1 > l2 ? l2 : l1; |
|||
} else { |
|||
var a = y0 - y1, b = x1 - x0, c = x0 * y1 - y0 * x1; |
|||
return Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b); |
|||
} |
|||
}; |
|||
|
|||
if (item) { |
|||
plot.showTooltip(item, pos); |
|||
} else if (that.plotOptions.series.lines.show && that.tooltipOptions.lines.track === true) { |
|||
var closestTrace = { |
|||
distance: -1 |
|||
}; |
|||
|
|||
$.each(plot.getData(), function (i, series) { |
|||
var xBeforeIndex = 0, |
|||
xAfterIndex = -1; |
|||
|
|||
// Our search here assumes our data is sorted via the x-axis.
|
|||
// TODO: Improve efficiency somehow - search smaller sets of data.
|
|||
for (var j = 1; j < series.data.length; j++) { |
|||
if (series.data[j - 1][0] <= pos.x && series.data[j][0] >= pos.x) { |
|||
xBeforeIndex = j - 1; |
|||
xAfterIndex = j; |
|||
} |
|||
} |
|||
|
|||
if (xAfterIndex === -1) { |
|||
plot.hideTooltip(); |
|||
return; |
|||
} |
|||
|
|||
var pointPrev = { x: series.data[xBeforeIndex][0], y: series.data[xBeforeIndex][1] }, |
|||
pointNext = { x: series.data[xAfterIndex][0], y: series.data[xAfterIndex][1] }; |
|||
|
|||
var distToLine = dotLineLength(pos.x, pos.y, pointPrev.x, pointPrev.y, pointNext.x, pointNext.y, false); |
|||
|
|||
if (distToLine < that.tooltipOptions.lines.threshold) { |
|||
|
|||
var closestIndex = lineDistance(pointPrev.x, pointPrev.y, pos.x, pos.y) < |
|||
lineDistance(pos.x, pos.y, pointNext.x, pointNext.y) ? xBeforeIndex : xAfterIndex; |
|||
|
|||
var pointSize = series.datapoints.pointsize; |
|||
|
|||
// Calculate the point on the line vertically closest to our cursor.
|
|||
var pointOnLine = [ |
|||
pos.x, |
|||
pointPrev.y + ((pointNext.y - pointPrev.y) * ((pos.x - pointPrev.x) / (pointNext.x - pointPrev.x))) |
|||
]; |
|||
|
|||
var item = { |
|||
datapoint: pointOnLine, |
|||
dataIndex: closestIndex, |
|||
series: series, |
|||
seriesIndex: i |
|||
}; |
|||
|
|||
if (closestTrace.distance === -1 || distToLine < closestTrace.distance) { |
|||
closestTrace = { |
|||
distance: distToLine, |
|||
item: item |
|||
}; |
|||
} |
|||
} |
|||
}); |
|||
|
|||
if (closestTrace.distance !== -1) |
|||
plot.showTooltip(closestTrace.item, pos); |
|||
else |
|||
plot.hideTooltip(); |
|||
} else { |
|||
plot.hideTooltip(); |
|||
} |
|||
} |
|||
|
|||
// Quick little function for setting the tooltip position.
|
|||
plot.setTooltipPosition = function (pos) { |
|||
var $tip = that.getDomElement(); |
|||
|
|||
var totalTipWidth = $tip.outerWidth() + that.tooltipOptions.shifts.x; |
|||
var totalTipHeight = $tip.outerHeight() + that.tooltipOptions.shifts.y; |
|||
if ((pos.x - $(window).scrollLeft()) > ($(window)[that.wfunc]() - totalTipWidth)) { |
|||
pos.x -= totalTipWidth; |
|||
} |
|||
if ((pos.y - $(window).scrollTop()) > ($(window)[that.hfunc]() - totalTipHeight)) { |
|||
pos.y -= totalTipHeight; |
|||
} |
|||
that.tipPosition.x = pos.x; |
|||
that.tipPosition.y = pos.y; |
|||
}; |
|||
|
|||
// Quick little function for showing the tooltip.
|
|||
plot.showTooltip = function (target, position) { |
|||
var $tip = that.getDomElement(); |
|||
|
|||
// convert tooltip content template to real tipText
|
|||
var tipText = that.stringFormat(that.tooltipOptions.content, target); |
|||
|
|||
$tip.html(tipText); |
|||
plot.setTooltipPosition({ x: position.pageX, y: position.pageY }); |
|||
$tip.css({ |
|||
left: that.tipPosition.x + that.tooltipOptions.shifts.x, |
|||
top: that.tipPosition.y + that.tooltipOptions.shifts.y |
|||
}).show(); |
|||
|
|||
// run callback
|
|||
if (typeof that.tooltipOptions.onHover === 'function') { |
|||
that.tooltipOptions.onHover(target, $tip); |
|||
} |
|||
}; |
|||
|
|||
// Quick little function for hiding the tooltip.
|
|||
plot.hideTooltip = function () { |
|||
that.getDomElement().hide().html(''); |
|||
}; |
|||
}; |
|||
|
|||
/** |
|||
* get or create tooltip DOM element |
|||
* @return jQuery object |
|||
*/ |
|||
FlotTooltip.prototype.getDomElement = function () { |
|||
var $tip = $('#' + this.tooltipOptions.id); |
|||
|
|||
if( $tip.length === 0 ){ |
|||
$tip = $('<div />').attr('id', this.tooltipOptions.id); |
|||
$tip.appendTo('body').hide().css({position: 'absolute'}); |
|||
|
|||
if(this.tooltipOptions.defaultTheme) { |
|||
$tip.css({ |
|||
'background': '#fff', |
|||
'z-index': '1040', |
|||
'padding': '0.4em 0.6em', |
|||
'border-radius': '0.5em', |
|||
'font-size': '0.8em', |
|||
'border': '1px solid #111', |
|||
'display': 'none', |
|||
'white-space': 'nowrap' |
|||
}); |
|||
} |
|||
} |
|||
|
|||
return $tip; |
|||
}; |
|||
|
|||
/** |
|||
* core function, create tooltip content |
|||
* @param {string} content - template with tooltip content |
|||
* @param {object} item - Flot item |
|||
* @return {string} real tooltip content for current item |
|||
*/ |
|||
FlotTooltip.prototype.stringFormat = function (content, item) { |
|||
|
|||
var percentPattern = /%p\.{0,1}(\d{0,})/; |
|||
var seriesPattern = /%s/; |
|||
var xLabelPattern = /%lx/; // requires flot-axislabels plugin https://github.com/markrcote/flot-axislabels, will be ignored if plugin isn't loaded
|
|||
var yLabelPattern = /%ly/; // requires flot-axislabels plugin https://github.com/markrcote/flot-axislabels, will be ignored if plugin isn't loaded
|
|||
var xPattern = /%x\.{0,1}(\d{0,})/; |
|||
var yPattern = /%y\.{0,1}(\d{0,})/; |
|||
var xPatternWithoutPrecision = "%x"; |
|||
var yPatternWithoutPrecision = "%y"; |
|||
var customTextPattern = "%ct"; |
|||
|
|||
var x, y, customText, p; |
|||
|
|||
// for threshold plugin we need to read data from different place
|
|||
if (typeof item.series.threshold !== "undefined") { |
|||
x = item.datapoint[0]; |
|||
y = item.datapoint[1]; |
|||
customText = item.datapoint[2]; |
|||
} else if (typeof item.series.lines !== "undefined" && item.series.lines.steps) { |
|||
x = item.series.datapoints.points[item.dataIndex * 2]; |
|||
y = item.series.datapoints.points[item.dataIndex * 2 + 1]; |
|||
// TODO: where to find custom text in this variant?
|
|||
customText = ""; |
|||
} else { |
|||
x = item.series.data[item.dataIndex][0]; |
|||
y = item.series.data[item.dataIndex][1]; |
|||
customText = item.series.data[item.dataIndex][2]; |
|||
} |
|||
|
|||
// I think this is only in case of threshold plugin
|
|||
if (item.series.label === null && item.series.originSeries) { |
|||
item.series.label = item.series.originSeries.label; |
|||
} |
|||
|
|||
// if it is a function callback get the content string
|
|||
if (typeof(content) === 'function') { |
|||
content = content(item.series.label, x, y, item); |
|||
} |
|||
|
|||
// percent match for pie charts and stacked percent
|
|||
if (typeof (item.series.percent) !== 'undefined') { |
|||
p = item.series.percent; |
|||
} else if (typeof (item.series.percents) !== 'undefined') { |
|||
p = item.series.percents[item.dataIndex]; |
|||
} |
|||
if (typeof p === 'number') { |
|||
content = this.adjustValPrecision(percentPattern, content, p); |
|||
} |
|||
|
|||
// series match
|
|||
if (typeof(item.series.label) !== 'undefined') { |
|||
content = content.replace(seriesPattern, item.series.label); |
|||
} else { |
|||
//remove %s if label is undefined
|
|||
content = content.replace(seriesPattern, ""); |
|||
} |
|||
|
|||
// x axis label match
|
|||
if (this.hasAxisLabel('xaxis', item)) { |
|||
content = content.replace(xLabelPattern, item.series.xaxis.options.axisLabel); |
|||
} else { |
|||
//remove %lx if axis label is undefined or axislabels plugin not present
|
|||
content = content.replace(xLabelPattern, ""); |
|||
} |
|||
|
|||
// y axis label match
|
|||
if (this.hasAxisLabel('yaxis', item)) { |
|||
content = content.replace(yLabelPattern, item.series.yaxis.options.axisLabel); |
|||
} else { |
|||
//remove %ly if axis label is undefined or axislabels plugin not present
|
|||
content = content.replace(yLabelPattern, ""); |
|||
} |
|||
|
|||
// time mode axes with custom dateFormat
|
|||
if (this.isTimeMode('xaxis', item) && this.isXDateFormat(item)) { |
|||
content = content.replace(xPattern, this.timestampToDate(x, this.tooltipOptions.xDateFormat, item.series.xaxis.options)); |
|||
} |
|||
if (this.isTimeMode('yaxis', item) && this.isYDateFormat(item)) { |
|||
content = content.replace(yPattern, this.timestampToDate(y, this.tooltipOptions.yDateFormat, item.series.yaxis.options)); |
|||
} |
|||
|
|||
// set precision if defined
|
|||
if (typeof x === 'number') { |
|||
content = this.adjustValPrecision(xPattern, content, x); |
|||
} |
|||
if (typeof y === 'number') { |
|||
content = this.adjustValPrecision(yPattern, content, y); |
|||
} |
|||
|
|||
// change x from number to given label, if given
|
|||
if (typeof item.series.xaxis.ticks !== 'undefined') { |
|||
|
|||
var ticks; |
|||
if (this.hasRotatedXAxisTicks(item)) { |
|||
// xaxis.ticks will be an empty array if tickRotor is being used, but the values are available in rotatedTicks
|
|||
ticks = 'rotatedTicks'; |
|||
} else { |
|||
ticks = 'ticks'; |
|||
} |
|||
|
|||
// see https://github.com/krzysu/flot.tooltip/issues/65
|
|||
var tickIndex = item.dataIndex + item.seriesIndex; |
|||
|
|||
if (item.series.xaxis[ticks].length > tickIndex && !this.isTimeMode('xaxis', item)) { |
|||
var valueX = (this.isCategoriesMode('xaxis', item)) ? item.series.xaxis[ticks][tickIndex].label : item.series.xaxis[ticks][tickIndex].v; |
|||
if (valueX === x) { |
|||
content = content.replace(xPattern, item.series.xaxis[ticks][tickIndex].label); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// change y from number to given label, if given
|
|||
if (typeof item.series.yaxis.ticks !== 'undefined') { |
|||
for (var index in item.series.yaxis.ticks) { |
|||
if (item.series.yaxis.ticks.hasOwnProperty(index)) { |
|||
var valueY = (this.isCategoriesMode('yaxis', item)) ? item.series.yaxis.ticks[index].label : item.series.yaxis.ticks[index].v; |
|||
if (valueY === y) { |
|||
content = content.replace(yPattern, item.series.yaxis.ticks[index].label); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// if no value customization, use tickFormatter by default
|
|||
if (typeof item.series.xaxis.tickFormatter !== 'undefined') { |
|||
//escape dollar
|
|||
content = content.replace(xPatternWithoutPrecision, item.series.xaxis.tickFormatter(x, item.series.xaxis).replace(/\$/g, '$$')); |
|||
} |
|||
if (typeof item.series.yaxis.tickFormatter !== 'undefined') { |
|||
//escape dollar
|
|||
content = content.replace(yPatternWithoutPrecision, item.series.yaxis.tickFormatter(y, item.series.yaxis).replace(/\$/g, '$$')); |
|||
} |
|||
|
|||
if (customText) |
|||
content = content.replace(customTextPattern, customText); |
|||
|
|||
return content; |
|||
}; |
|||
|
|||
// helpers just for readability
|
|||
FlotTooltip.prototype.isTimeMode = function (axisName, item) { |
|||
return (typeof item.series[axisName].options.mode !== 'undefined' && item.series[axisName].options.mode === 'time'); |
|||
}; |
|||
|
|||
FlotTooltip.prototype.isXDateFormat = function (item) { |
|||
return (typeof this.tooltipOptions.xDateFormat !== 'undefined' && this.tooltipOptions.xDateFormat !== null); |
|||
}; |
|||
|
|||
FlotTooltip.prototype.isYDateFormat = function (item) { |
|||
return (typeof this.tooltipOptions.yDateFormat !== 'undefined' && this.tooltipOptions.yDateFormat !== null); |
|||
}; |
|||
|
|||
FlotTooltip.prototype.isCategoriesMode = function (axisName, item) { |
|||
return (typeof item.series[axisName].options.mode !== 'undefined' && item.series[axisName].options.mode === 'categories'); |
|||
}; |
|||
|
|||
//
|
|||
FlotTooltip.prototype.timestampToDate = function (tmst, dateFormat, options) { |
|||
var theDate = $.plot.dateGenerator(tmst, options); |
|||
return $.plot.formatDate(theDate, dateFormat, this.tooltipOptions.monthNames, this.tooltipOptions.dayNames); |
|||
}; |
|||
|
|||
//
|
|||
FlotTooltip.prototype.adjustValPrecision = function (pattern, content, value) { |
|||
|
|||
var precision; |
|||
var matchResult = content.match(pattern); |
|||
if( matchResult !== null ) { |
|||
if(RegExp.$1 !== '') { |
|||
precision = RegExp.$1; |
|||
value = value.toFixed(precision); |
|||
|
|||
// only replace content if precision exists, in other case use thickformater
|
|||
content = content.replace(pattern, value); |
|||
} |
|||
} |
|||
return content; |
|||
}; |
|||
|
|||
// other plugins detection below
|
|||
|
|||
// check if flot-axislabels plugin (https://github.com/markrcote/flot-axislabels) is used and that an axis label is given
|
|||
FlotTooltip.prototype.hasAxisLabel = function (axisName, item) { |
|||
return ($.inArray(this.plotPlugins, 'axisLabels') !== -1 && typeof item.series[axisName].options.axisLabel !== 'undefined' && item.series[axisName].options.axisLabel.length > 0); |
|||
}; |
|||
|
|||
// check whether flot-tickRotor, a plugin which allows rotation of X-axis ticks, is being used
|
|||
FlotTooltip.prototype.hasRotatedXAxisTicks = function (item) { |
|||
return ($.inArray(this.plotPlugins, 'tickRotor') !== -1 && typeof item.series.xaxis.rotatedTicks !== 'undefined'); |
|||
}; |
|||
|
|||
//
|
|||
var init = function (plot) { |
|||
new FlotTooltip(plot); |
|||
}; |
|||
|
|||
// define Flot plugin
|
|||
$.plot.plugins.push({ |
|||
init: init, |
|||
options: defaultOptions, |
|||
name: 'tooltip', |
|||
version: '0.8.4' |
|||
}); |
|||
|
|||
})(jQuery); |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue