diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3729ff0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/.github/workflows/deploy-docs-and-dashbaord.yml b/.github/workflows/deploy-docs-and-dashbaord.yml new file mode 100644 index 0000000..1463656 --- /dev/null +++ b/.github/workflows/deploy-docs-and-dashbaord.yml @@ -0,0 +1,66 @@ +name: Publish docs & Build dashboard +on: + push: + branches: + - master +jobs: + changes: + runs-on: ubuntu-latest + outputs: + docs: ${{ steps.filter.outputs.docs }} + dashboard: ${{ steps.filter.outputs.dashboard }} + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v2 + with: + persist-credentials: false + - name: Check for changes 🎯 + uses: dorny/paths-filter@v2 + id: filter + with: + filters: | + docs: + - 'docs/**' + dashboard: + - 'src/DotNetCore.CAP.Dashboard/wwwroot/src/**' + + build-dashbaord-and-push: + needs: changes + if: ${{ needs.changes.outputs.dashboard == 'true' }} + runs-on: ubuntu-latest + defaults: + run: + working-directory: src/DotNetCore.CAP.Dashboard/wwwroot + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v2 + with: + persist-credentials: false + - name: Use Node.js 🥽 + uses: actions/setup-node@v1 + with: + node-version: '14.x' + - name: Install dependencies 🧵 + run: npm install + - name: Build to dist 🧨 + run: npm run build + - name: Commit & Push dist changes 🚀 + uses: actions-js/push@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + + build-docs-and-deploy: + needs: changes + if: ${{ needs.changes.outputs.docs == 'true' }} + runs-on: ubuntu-latest + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v2 + with: + persist-credentials: false + - name: Deploy docs 🚀 + uses: mhausenblas/mkdocs-deploy-gh-pages@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GOOGLE_ANALYTICS_KEY: ${{ secrets.GOOGLE_ANALYTICS_KEY }} + CONFIG_FILE: docs/mkdocs.yml diff --git a/.gitignore b/.gitignore index cef1711..8b7994a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -[Oo]bj/ +node_modules +[Oo]bj/ [Bb]in/ TestResults/ .nuget/ @@ -12,8 +13,6 @@ PublishProfiles/ *.docstates _ReSharper.* nuget.exe -*net45.csproj -*net451.csproj *k10.csproj *.psess *.vsp @@ -24,21 +23,11 @@ nuget.exe *.*sdf *.ipch *.sln.ide -project.lock.json .vs .build/ -.testPublish/ -obj/ -bin/ -/.idea/.idea.CAP /.idea/.idea.CAP /.idea Properties /pack.bat -/src/DotNetCore.CAP/project.json -/src/DotNetCore.CAP/packages.config -/src/DotNetCore.CAP/DotNetCore.CAP.Net47.csproj -/NuGet.config .vscode/* -samples/Sample.RabbitMQ.MongoDB/appsettings.Development.json site/ diff --git a/.travis.yml b/.travis.yml index 1a12425..61c3e38 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: csharp sudo: required dist: xenial solution: CAP.sln -dotnet: 3.1.100 +dotnet: 5.0.100 mono: none env: - Cap_MySql_ConnectionString="Server=127.0.0.1;Database=cap_test;Uid=root;Pwd=;Allow User Variables=True;SslMode=none" @@ -12,5 +12,5 @@ services: script: - export PATH="$PATH:$HOME/.dotnet/tools" - - dotnet tool install --global FlubuCore.GlobalTool --version 5.1.1 + - dotnet tool install --global FlubuCore.GlobalTool --version 6.1.0 - flubu build tests \ No newline at end of file diff --git a/CAP.sln b/CAP.sln index 74a31fb..866c53b 100644 --- a/CAP.sln +++ b/CAP.sln @@ -13,6 +13,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .gitignore = .gitignore .travis.yml = .travis.yml appveyor.yml = appveyor.yml + .github\workflows\deploy-docs-and-dashbaord.yml = .github\workflows\deploy-docs-and-dashbaord.yml .github\ISSUE_TEMPLATE = .github\ISSUE_TEMPLATE LICENSE.txt = LICENSE.txt README.md = README.md @@ -67,7 +68,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.AmazonSQS", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.AmazonSQS.InMemory", "samples\Sample.AmazonSQS.InMemory\Sample.AmazonSQS.InMemory.csproj", "{B187DD15-092D-4B72-9807-50856607D237}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.Pulsar", "src\DotNetCore.CAP.Pulsar\DotNetCore.CAP.Pulsar.csproj", "{33C48DD1-5B7D-475B-B849-FFE1D9A4FBD1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.NATS", "src\DotNetCore.CAP.NATS\DotNetCore.CAP.NATS.csproj", "{8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.Redis.SqlServer", "samples\Samples.Redis.SqlServer\Samples.Redis.SqlServer.csproj", "{375AF85D-8C81-47C6-BE5B-D0874D4971EA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.RedisStreams", "src\DotNetCore.CAP.RedisStreams\DotNetCore.CAP.RedisStreams.csproj", "{54458B54-49CC-454C-82B2-4AED681D9D07}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Dashboard.Auth", "samples\Sample.Dashboard.Auth\Sample.Dashboard.Auth.csproj", "{6E059983-DE89-4D53-88F5-D9083BCE257F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.MultiModuleSubscriberTests", "test\DotNetCore.CAP.MultiModuleSubscriberTests\DotNetCore.CAP.MultiModuleSubscriberTests.csproj", "{23684403-7DA8-489A-8A1E-8056D7683E18}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.Pulsar", "src\DotNetCore.CAP.Pulsar\DotNetCore.CAP.Pulsar.csproj", "{AB7A10CB-2C7E-49CE-AA21-893772FF6546}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -155,10 +166,30 @@ Global {B187DD15-092D-4B72-9807-50856607D237}.Debug|Any CPU.Build.0 = Debug|Any CPU {B187DD15-092D-4B72-9807-50856607D237}.Release|Any CPU.ActiveCfg = Release|Any CPU {B187DD15-092D-4B72-9807-50856607D237}.Release|Any CPU.Build.0 = Release|Any CPU - {33C48DD1-5B7D-475B-B849-FFE1D9A4FBD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {33C48DD1-5B7D-475B-B849-FFE1D9A4FBD1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {33C48DD1-5B7D-475B-B849-FFE1D9A4FBD1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {33C48DD1-5B7D-475B-B849-FFE1D9A4FBD1}.Release|Any CPU.Build.0 = Release|Any CPU + {8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}.Release|Any CPU.Build.0 = Release|Any CPU + {375AF85D-8C81-47C6-BE5B-D0874D4971EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {375AF85D-8C81-47C6-BE5B-D0874D4971EA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {375AF85D-8C81-47C6-BE5B-D0874D4971EA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {375AF85D-8C81-47C6-BE5B-D0874D4971EA}.Release|Any CPU.Build.0 = Release|Any CPU + {54458B54-49CC-454C-82B2-4AED681D9D07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54458B54-49CC-454C-82B2-4AED681D9D07}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54458B54-49CC-454C-82B2-4AED681D9D07}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54458B54-49CC-454C-82B2-4AED681D9D07}.Release|Any CPU.Build.0 = Release|Any CPU + {6E059983-DE89-4D53-88F5-D9083BCE257F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E059983-DE89-4D53-88F5-D9083BCE257F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E059983-DE89-4D53-88F5-D9083BCE257F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E059983-DE89-4D53-88F5-D9083BCE257F}.Release|Any CPU.Build.0 = Release|Any CPU + {23684403-7DA8-489A-8A1E-8056D7683E18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23684403-7DA8-489A-8A1E-8056D7683E18}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23684403-7DA8-489A-8A1E-8056D7683E18}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23684403-7DA8-489A-8A1E-8056D7683E18}.Release|Any CPU.Build.0 = Release|Any CPU + {AB7A10CB-2C7E-49CE-AA21-893772FF6546}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB7A10CB-2C7E-49CE-AA21-893772FF6546}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB7A10CB-2C7E-49CE-AA21-893772FF6546}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB7A10CB-2C7E-49CE-AA21-893772FF6546}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -184,7 +215,12 @@ Global {2B0F467E-ABBD-4A51-BF38-D4F609DB6266} = {3A6B6931-A123-477A-9469-8B468B5385AF} {43475E00-51B7-443D-BC2D-FC21F9D8A0B4} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} {B187DD15-092D-4B72-9807-50856607D237} = {3A6B6931-A123-477A-9469-8B468B5385AF} - {33C48DD1-5B7D-475B-B849-FFE1D9A4FBD1} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} + {8B2FD3EA-E72B-4A82-B182-B87EC0C15D07} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} + {375AF85D-8C81-47C6-BE5B-D0874D4971EA} = {3A6B6931-A123-477A-9469-8B468B5385AF} + {54458B54-49CC-454C-82B2-4AED681D9D07} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} + {6E059983-DE89-4D53-88F5-D9083BCE257F} = {3A6B6931-A123-477A-9469-8B468B5385AF} + {23684403-7DA8-489A-8A1E-8056D7683E18} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} + {AB7A10CB-2C7E-49CE-AA21-893772FF6546} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2E70565D-94CF-40B4-BFE1-AC18D5F736AB} diff --git a/CAP.sln.DotSettings b/CAP.sln.DotSettings index 486eace..fafa21d 100644 --- a/CAP.sln.DotSettings +++ b/CAP.sln.DotSettings @@ -1,5 +1,7 @@  DB + NATS SNS True + True True \ No newline at end of file diff --git a/README.md b/README.md index 7949dd2..437b891 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,13 @@ CAP can be installed in your project with the following command. PM> Install-Package DotNetCore.CAP ``` -CAP supports RabbitMQ, Kafka and AzureService as message queue, following packages are available to install: +CAP supports RabbitMQ, Kafka, AzureService, AmazonSQS as message queue, following packages are available to install: ``` PM> Install-Package DotNetCore.CAP.Kafka PM> Install-Package DotNetCore.CAP.RabbitMQ PM> Install-Package DotNetCore.CAP.AzureServiceBus +PM> Install-Package DotNetCore.CAP.AmazonSQS ``` CAP supports SqlServer, MySql, PostgreSql,MongoDB as event log storage. @@ -80,6 +81,7 @@ public void ConfigureServices(IServiceCollection services) x.UseRabbitMQ("ConnectionString"); x.UseKafka("ConnectionString"); x.UseAzureServiceBus("ConnectionString"); + x.UseAmazonSQS(); }); } @@ -239,7 +241,7 @@ services.AddCap(x => ### Dashboard -CAP v2.1+ provides dashboard pages, you can easily view messages that were sent and received. In addition, you can also view the message status in real time in the dashboard. Use the following command to install the Dashboard in your project. +CAP also provides dashboard pages, you can easily view messages that were sent and received. In addition, you can also view the message status in real time in the dashboard. Use the following command to install the Dashboard in your project. ``` PM> Install-Package DotNetCore.CAP.Dashboard @@ -270,14 +272,6 @@ services.AddCap(x => The default dashboard address is :[http://localhost:xxx/cap](http://localhost:xxx/cap), you can configure relative path `/cap` with `x.UseDashboard(opt =>{ opt.MatchPath="/mycap"; })`. -![dashboard](http://images2017.cnblogs.com/blog/250417/201710/250417-20171004220827302-189215107.png) - -![received](http://images2017.cnblogs.com/blog/250417/201710/250417-20171004220934115-1107747665.png) - -![subscibers](http://images2017.cnblogs.com/blog/250417/201710/250417-20171004220949193-884674167.png) - -![nodes](http://images2017.cnblogs.com/blog/250417/201710/250417-20171004221001880-1162918362.png) - ## Contribute diff --git a/README.zh-cn.md b/README.zh-cn.md index b5efb90..7c4a9e3 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -21,7 +21,7 @@ CAP 采用的是和当前数据库集成的本地消息表的方案来解决在 你同样可以把 CAP 当做 EventBus 来使用,CAP提供了一种更加简单的方式来实现事件消息的发布和订阅,在订阅以及发布的过程中,你不需要继承或实现任何接口。 -这是CAP集在ASP.NET Core 微服务架构中的一个示意图: +这是 CAP 集在ASP.NET Core 微服务架构中的一个示意图: ## 架构预览 @@ -39,12 +39,13 @@ CAP 采用的是和当前数据库集成的本地消息表的方案来解决在 PM> Install-Package DotNetCore.CAP ``` -CAP 支持 Kafka、RabbitMQ、AzureServiceBus 等消息队列,你可以按需选择下面的包进行安装: +CAP 支持 Kafka、RabbitMQ、AzureServiceBus、AmazonSQS 等消息队列,你可以按需选择下面的包进行安装: ``` PM> Install-Package DotNetCore.CAP.Kafka PM> Install-Package DotNetCore.CAP.RabbitMQ PM> Install-Package DotNetCore.CAP.AzureServiceBus +PM> Install-Package DotNetCore.CAP.AmazonSQS ``` CAP 提供了 Sql Server, MySql, PostgreSQL,MongoDB 的扩展作为数据库存储: @@ -81,10 +82,11 @@ public void ConfigureServices(IServiceCollection services) //如果你使用的 MongoDB,你可以添加如下配置: x.UseMongoDB("ConnectionStrings"); //注意,仅支持MongoDB 4.0+集群 - //CAP支持 RabbitMQ、Kafka、AzureServiceBus 等作为MQ,根据使用选择配置: + //CAP支持 RabbitMQ、Kafka、AzureServiceBus、AmazonSQS 等作为MQ,根据使用选择配置: x.UseRabbitMQ("ConnectionStrings"); x.UseKafka("ConnectionStrings"); x.UseAzureServiceBus("ConnectionStrings"); + x.UseAmazonSQS(); }); } @@ -237,7 +239,7 @@ services.AddCap(x => ### Dashboard -CAP 2.1+ 以上版本中提供了仪表盘(Dashboard)功能,你可以很方便的查看发出和接收到的消息。除此之外,你还可以在仪表盘中实时查看发送或者接收到的消息。 +CAP 同时提供了仪表盘(Dashboard)功能,你可以很方便的查看发出和接收到的消息。 除此之外,你还可以在仪表盘中实时查看发送或者接收到的消息。 使用一下命令安装 Dashboard: diff --git a/appveyor.yml b/appveyor.yml index 018ca7a..2e01eda 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,7 @@ environment: services: - mysql before_build: - - ps: dotnet tool install --global FlubuCore.GlobalTool --version 5.1.1 + - ps: dotnet tool install --global FlubuCore.GlobalTool --version 6.1.0 build_script: - ps: flubu test: off @@ -18,6 +18,6 @@ deploy: on: appveyor_repo_tag: true api_key: - secure: PZXRBOGLyhYLP7ulHfrh6MnkqB8CstuitgbLcJr3cZkLJLLzPH0ahvuTtmhWxtR2 + secure: Q/7aMFCMA363iQv1r2fgW2PyvAFL3H409s9Pq8SgmYAbTH+c6ZQcYC9evHpipuNR skip_symbols: false artifact: /artifacts\/.+\.s?nupkg/ diff --git a/build/BuildScript.Version.cs b/build/BuildScript.Version.cs index aed224f..96e0cff 100644 --- a/build/BuildScript.Version.cs +++ b/build/BuildScript.Version.cs @@ -1,5 +1,4 @@ -using System; -using System.IO; +using System.IO; using System.Xml; using FlubuCore.Context; using FlubuCore.Scripting.Attributes; @@ -11,7 +10,7 @@ namespace BuildScript { public BuildVersion FetchBuildVersion(ITaskContext context) { - var content = System.IO.File.ReadAllText(RootDirectory.CombineWith("build/version.props")); + var content = File.ReadAllText(RootDirectory.CombineWith("build/version.props")); XmlDocument doc = new XmlDocument(); doc.LoadXml(content); @@ -26,13 +25,12 @@ namespace BuildScript bool isCi = false; bool isTagged = false; - if (!context.BuildSystems().IsLocalBuild) + if (!context.BuildServers().IsLocalBuild) { isCi = true; - bool isTagAppveyor = context.BuildSystems().AppVeyor().IsTag; - - if (context.BuildSystems().RunningOn == BuildSystemType.AppVeyor && isTagAppveyor || - context.BuildSystems().RunningOn == BuildSystemType.TravisCI && string.IsNullOrWhiteSpace(context.BuildSystems().Travis().TagName)) + bool isTagAppveyor = context.BuildServers().AppVeyor().IsTag; + if (context.BuildServers().RunningOn == BuildServerType.AppVeyor && isTagAppveyor || + context.BuildServers().RunningOn == BuildServerType.TravisCI && string.IsNullOrWhiteSpace(context.BuildServers().Travis().TagName)) { isTagged = true; } diff --git a/build/BuildScript.csproj b/build/BuildScript.csproj index bb5c75c..1c36326 100644 --- a/build/BuildScript.csproj +++ b/build/BuildScript.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.1 @@ -9,7 +9,7 @@ - + diff --git a/build/version.props b/build/version.props index 933f022..2181d53 100644 --- a/build/version.props +++ b/build/version.props @@ -1,8 +1,8 @@ - 3 + 5 1 - 0 + 4 $(VersionMajor).$(VersionMinor).$(VersionPatch) diff --git a/docs/content/about/release-notes.md b/docs/content/about/release-notes.md index 35f03d8..8172c01 100644 --- a/docs/content/about/release-notes.md +++ b/docs/content/about/release-notes.md @@ -1,5 +1,154 @@ # Release Notes + +## Version 5.1.2 (2021-07-26) + +**Bug Fixed:** + +* Fixed consumer register cancellation token source null referencee bug. (#952) +* Fixed redis streams transport cluster keys cross-hashslot bug. (#944) + + +## Version 5.1.1 (2021-07-09) + +**Features:** + +* Improve flow control for message cache of in memory. (#935) +* Add cancellation token support to subscribers. (#912) +* Add pathbase options for dashbaord. (#901) +* Add custom authorization scheme support for dashbaord. (#906) + +**Bug Fixed:** + +* Fixed mysql connect timeout expired bug. (#931) +* Fixed consul health check path invalid bug. (#921) +* Fixed mongo dashbaord query bug. (#909) + +## Version 5.1.0 (2021-06-07) + +**Features:** + +* Add configure options for json serialization. (#879) +* Add Redis Streams transport support. (#817) +* New dashboard build with vue. (#880) +* Add subscribe filter support. (#894) + +**Bug Fixed:** + +* Fixed use CapEFDbTransaction to get dbtransaction extension method bug. (#868) +* Fixed pending message has not been deleted from buffer list in SQL Server. (#889) +* Fixed dispatcher processing when storage message exception bug. (#900) + + +## Version 5.0.3 (2021-05-14) + +**Bug Fixed:** + +* Fix the bug of getting db transaction through the IDbContextTransaction for SQLServer. (#867) +* Fix RabbitMQ Connection close forced. (#861) + +## Version 5.0.2 (2021-04-28) + +**Features:** + +* Add support for Azure Service Bus sessions. (#829) +* Add custom message headers support for RabbitMQ consumer. (#818) + +**Bug Fixed:** + +* Downgrading Microsoft.Data.SqlClient to 2.0.1. (#839) +* DiagnosticObserver does not use null connection. (#845) +* Fix null reference in AmazonSQSTransport. (#846) + +## Version 5.0.1 (2021-04-07) + +**Features:** + +* Add KafkaOptions.MainConfig to AutoCreateTopic. (#810) +* Add support rewriting the default configuration of Kafka consumer. (#822) +* Add DefaultChallengeScheme dashboard options to specify dashboard auth challenge scheme. (#815) + +**Bug Fixed:** + +* Fixed topic selector in IConsumerServiceSelector. (#806) +* Update AWS topic subscription and SQS access policy generation. (#808) +* Fixed memory leak when using transction to publish message. (#816) +* Fixed SQL content filter on IMonitoringApi.PostgreSql.cs. (#814) +* Fixed the expiration time display problem in the dashboard due to time zone issues (#820) +* Fixed the creation timing of Kafka automatically creating Topic. (#823) +* Fixed Dashboard metric not update. (#819) + +## Version 5.0.0 (2021-03-23) + +**Features:** + +* Upgrade to .NET Standard 2.1 and support .NET 5. (#727) +* Replace Newtonsoft.Json to System.Text.Json. (#740) +* Support NATS Transport. (#595,#743) +* Enabling publiser confirms for RabbitMQ. (#730) +* Support query subscription from DI implementation factory. (#756) +* Add options to create lazy queue for RabbitMQ. (#772) +* Support to add custom tags for Consul. (#786) +* Support custom group and topic prefiex. (#780) +* Renemae DefaultGroup option to DefaultGroupName. +* Add auto create topic at startup for Kafka. (#795,#744) + +**Bug Fixed:** + +* Fixed retrying process earlier than consumer registration to DI. (#760) +* Fixed Amazon SQS missing pagination topics. (#765) +* Fixed RabbitMQ MessageTTL option to int type. (#787) +* Fixed Dashboard auth. (#793) +* Fixed ClientProvidedName could not be renamed for RabbitMQ. (#791) +* Fixed EntityFramework transaction will not rollback when exception occurred. (#798) + +## Version 3.1.2 (2020-12-03) + +**Features:** +* Support record the exception message in the headers. (#679) +* Support consul service check for https. (#722) +* Support custom producer threads count options for sending. (#731) +* Upgrade dependent nuget packages to latest. + +**Bug Fixed:** + +* Fixed InmemoryQueue expired messages are not removed bug. (#691) +* Fixed Executor key change lead to possible null reference exception. (#698) +* Fixed Postgresql delete expires data logic error. (#714) + +## Version 3.1.1 (2020-09-23) + +**Features:** + +* Add consumer parameter with interface suppport. (#669) +* Add custom correlation id and message id support. (#668) +* Enhanced custom serialization support. (#641) + +**Bug Fixed:** + +* Solve the issue of being duplicated executors from different assemblies. (#666) +* Added comparer to remove duplicate ConsumerExecutors. (#653) +* Add re-enable the auto create topics configuration item for Kafka, it's false by default. now is true. (#635) +* Fixed postgresql transaction rollback invoke bug. (#640) +* Fixed SQLServer table name customize bug. (#632) + +## Version 3.1.0 (2020-08-15) + +**Features:** + +* Add Amazon SQS support. (#597) +* Remove Dapper and replace with ADO.NET in storage project. (#583) +* Add debug symbols package to nuget. +* Upgrade dependent nuget package version to latest. +* English docs grammar correction. Thanks @mzorec + +**Bug Fixed:** + +* Fix mysql transaction rollback bug. (#598) +* Fix dashboard query bug. (#600) +* Fix mongo db query bug. (#611) +* Fix dashboard browser language detection bug. (#631) + ## Version 3.0.4 (2020-05-27) **Bug Fixed:** diff --git a/docs/content/img/favicon.ico b/docs/content/img/favicon.ico index 97f4a40..73fc309 100644 Binary files a/docs/content/img/favicon.ico and b/docs/content/img/favicon.ico differ diff --git a/docs/content/user-guide/en/cap/configuration.md b/docs/content/user-guide/en/cap/configuration.md index 65000d2..8125c6b 100644 --- a/docs/content/user-guide/en/cap/configuration.md +++ b/docs/content/user-guide/en/cap/configuration.md @@ -31,7 +31,7 @@ For specific transport and storage configuration, you can take a look at the con The `CapOptions` is used to store configuration information. By default they have default values, sometimes you may need to customize them. -#### DefaultGroup +#### DefaultGroupName > Default: cap.queue.{assembly name} @@ -41,6 +41,20 @@ The default consumer group name, corresponds to different names in different Tra Map to [Queue Names](https://www.rabbitmq.com/queues.html#names) in RabbitMQ. Map to [Consumer Group Id](http://kafka.apache.org/documentation/#group.id) in Apache Kafka. Map to Subscription Name in Azure Service Bus. + Map to [Queue Group Name](https://docs.nats.io/nats-concepts/queue) in NATS. + Map to [Consumer Group](https://redis.io/topics/streams-intro#creating-a-consumer-group) in Redis Streams. + +#### GroupNamePrefix + +> Default: Null + +Add unified prefixes for consumer group. https://github.com/dotnetcore/CAP/pull/780 + +#### TopicNamePrefix + +> Default: Null + +Add unified prefixes for topic/queue name. https://github.com/dotnetcore/CAP/pull/780 #### Versioning @@ -71,11 +85,17 @@ During the message sending process if consumption method fails, CAP will try to By default if failure occurs on send or consume, retry will start after **4 minutes** in order to avoid possible problems caused by setting message state delays. Failures in the process of sending and consuming messages will be retried 3 times immediately, and will be retried polling after 3 times, at which point the FailedRetryInterval configuration will take effect. +#### CollectorCleaningInterval + +> Default: 300 sec + +The interval of the collector processor deletes expired messages. + #### ConsumerThreadCount > Default : 1 -Number of consumer threads, when this value is greater than 1, the order of message execution cannot be guaranteed +Number of consumer threads, when this value is greater than 1, the order of message execution cannot be guaranteed. #### FailedRetryCount @@ -87,14 +107,9 @@ Maximum number of retries. When this value is reached, retry will stop and the m > Default: NULL -Type: `Action` - -> -T1 : Message Type -T2 : Message Name -T3 : Message Content +Type: `Action` -Failure threshold callback. This action is called when the retry reaches the value set by `FailedRetryCount`, you can receive notification by specifying this parameter to make a manual intervention. For example, send an email or notification. +Failure threshold callback. This action is called when the retry reaches the value set by `FailedRetryCount`, you can receive notification by specifying this parameter to make a manual intervention. For example, send an email or notification. #### SucceedMessageExpiredAfter diff --git a/docs/content/user-guide/en/cap/filter.md b/docs/content/user-guide/en/cap/filter.md new file mode 100644 index 0000000..6bfcb26 --- /dev/null +++ b/docs/content/user-guide/en/cap/filter.md @@ -0,0 +1,54 @@ +# Filter + +Subscriber filters are similar to ASP.NET MVC filters and are mainly used to process additional work before and after the subscriber method is executed. Such as transaction management or logging, etc. + +## Create subscribe filter + +### Create Filter + +Create a new filter class and inherit the `SubscribeFilter` abstract class. + +```C# +public class MyCapFilter: SubscribeFilter +{ + public override void OnSubscribeExecuting(ExecutingContext context) + { + // before subscribe method exectuing + } + + public override void OnSubscribeExecuted(ExecutedContext context) + { + // after subscribe method executed + } + + public override void OnSubscribeException(ExceptionContext context) + { + // subscribe method execution exception + } +} +``` + +In some scenarios, if you want to terminate the subscriber method execution, you can throw an exception in `OnSubscribeExecuting`, and choose to ignore the exception in `OnSubscribeException`. + +To ignore exceptions, you can setting `context.ExceptionHandled = true` in `ExceptionContext` + + +```C# +public override void OnSubscribeException(ExceptionContext context) +{ + context.ExceptionHandled = true; +} +``` + +### Configuration Filter + +Use `AddSubscribeFilter<>` to add a filter. + +```C# +services.AddCap(opt => +{ + // *** +}.AddSubscribeFilter(); +``` + +Currently, we do not support adding multiple filters. diff --git a/docs/content/user-guide/en/cap/messaging.md b/docs/content/user-guide/en/cap/messaging.md index 169549b..dee83c6 100644 --- a/docs/content/user-guide/en/cap/messaging.md +++ b/docs/content/user-guide/en/cap/messaging.md @@ -2,6 +2,95 @@ The data sent by using the `ICapPublisher` interface is called `Message`. +## Compensating transaction + +Wiki : +[Compensating transaction](https://en.wikipedia.org/wiki/Compensating_transaction) + +In some cases, consumers need to return the execution value to tell the publisher, so that the publisher can implement some compensation actions, usually we called message compensation. + +Usually you can notify the upstream by republishing a new message in the consumer code. CAP provides a simple way to do this. You can specify `callbackName` parameter when publishing message, usually this only applies to point-to-point consumption. The following is an example. + +For example, in an e-commerce application, the initial status of the order is pending, and the status is marked as succeeded when the product quantity is successfully deducted, otherwise it is failed. + +```C# +// ============= Publisher ================= + +_capBus.Publish("place.order.qty.deducted", + contentObj: new { OrderId = 1234, ProductId = 23255, Qty = 1 }, + callbackName: "place.order.mark.status"); + +// publisher using `callbackName` to subscribe consumer result + +[CapSubscribe("place.order.mark.status")] +public void MarkOrderStatus(JsonElement param) +{ + var orderId = param.GetProperty("OrderId").GetInt32(); + var isSuccess = param.GetProperty("IsSuccess").GetBoolean(); + + if(isSuccess){ + // mark order status to succeeded + } + else{ + // mark order status to failed + } +} + +// ============= Consumer =================== + +[CapSubscribe("place.order.qty.deducted")] +public object DeductProductQty(JsonElement param) +{ + var orderId = param.GetProperty("OrderId").GetInt32(); + var productId = param.GetProperty("ProductId").GetInt32(); + var qty = param.GetProperty("Qty").GetInt32(); + + //business logic + + return new { OrderId = orderId, IsSuccess = true }; +} +``` + +## Heterogeneous system integration + +In version 3.0+, we reconstructed the message structure. We used the Header in the message protocol in the message queue to transmit some additional information, so that we can do it in the Body without modifying or packaging the user’s original The message data format and content are sent. + +This approach is reasonable. It helps to better integrate in heterogeneous systems. Compared with previous versions, users do not need to know the message structure used inside CAP to complete the integration work. + +Now we divide the message into Header and Body for transmission. + +The data in the body is the content of the original message sent by the user, that is, the content sent by calling the Publish method. We do not perform any packaging, but send it to the message queue after serialization. + +In the Header, we need to pass some additional information so that the CAP can extract the key features for operation when the message is received. + +The following is the content that needs to be written into the header of the message when sending a message in a heterogeneous system: + + Key | DataType | Description +-- | --| -- +cap-msg-id | string | Message Id, Generated by snowflake algorithm, can also be guid +cap-msg-name | string | The name of the message +cap-msg-type | string | The type of message, `typeof(T).FullName`(not required) +cap-senttime | string | sending time (not required) + +### Custom headers +To consume messages sent without CAP headers, both Kafka and RabbitMQ consumers can inject a minimal set of headers using custom headers as shown below: +```C# +container.AddCap(x => +{ + x.UseRabbitMQ(z => + { + z.ExchangeName = "TestExchange"; + z.CustomHeaders = e => new List> + { + new KeyValuePair(DotNetCore.CAP.Messages.Headers.MessageId, SnowflakeId.Default().NextId().ToString()), + new KeyValuePair(DotNetCore.CAP.Messages.Headers.MessageName, e.RoutingKey) + }; + }); +}); +``` + +After adding `cap-msg-id` and `cap-msg-name`, CAP consumers receive messages sent directly from the RabbitMQ management tool. + ## Scheduling After CAP receives a message, it sends the message to Transport(RabitMq, Kafka...), which is transported by transport. @@ -22,7 +111,7 @@ Retrying plays an important role in the overall CAP architecture design, CAP ret During the message sending process, when the broker crashes or the connection fails or an abnormality occurs, CAP will retry the sending. Retry 3 times for the first time, retry every minute after 4 minutes, and +1 retry. When the total number of retries reaches 50,CAP will stop retrying. -You can adjust the total number of retries by setting `FailedRetryCount` in CapOptions. +You can adjust the total number of retries by setting [FailedRetryCount](../configuration#failedretrycount) in CapOptions. It will stop when the maximum number of times is reached. You can see the reason for the failure in Dashboard and choose whether to manually retry. @@ -36,6 +125,8 @@ There is an `ExpiresAt` field in the database message table indicating the expir Consuming failure will change the message status to `Failed` and `ExpiresAt` will be set to **15 days** later. -By default, the data of the message in the table is deleted **every hour** to avoid performance degradation caused by too much data. The cleanup strategy `ExpiresAt` is performed when field is not empty and is less than the current time. +By default, the data of the message in the table is deleted **5 minutes** to avoid performance degradation caused by too much data. The cleanup strategy `ExpiresAt` is performed when field is not empty and is less than the current time. + +That is to say, the message with the status Failed (by default they have been retried 50 times), if you do not have manual intervention for 15 days, it will **also be** cleaned up. -That is to say, the message with the status Failed (by default they have been retried 50 times), if you do not have manual intervention for 15 days, it will **also be** cleaned up. \ No newline at end of file +You can use [CollectorCleaningInterval](../configuration#collectorcleaninginterval) configuration items to custom the interval time. \ No newline at end of file diff --git a/docs/content/user-guide/en/cap/sagas.md b/docs/content/user-guide/en/cap/sagas.md deleted file mode 100644 index 50f4402..0000000 --- a/docs/content/user-guide/en/cap/sagas.md +++ /dev/null @@ -1,7 +0,0 @@ -# Sagas - -Sagas (also known in the literature as "process managers") are stateful services. You can think of them as state machines whose transitions are driven by messages. - -## Sagas on CAP - -TODO \ No newline at end of file diff --git a/docs/content/user-guide/en/getting-started/introduction.md b/docs/content/user-guide/en/getting-started/introduction.md index 1446144..eaf4cd4 100644 --- a/docs/content/user-guide/en/getting-started/introduction.md +++ b/docs/content/user-guide/en/getting-started/introduction.md @@ -25,6 +25,8 @@ CAP is modular in design and highly scalable. You have many options to choose fr [Article: Introduction and how to use](http://www.cnblogs.com/savorboard/p/cap.html) +[Article: New features in version 5.0](https://www.cnblogs.com/savorboard/p/cap-5-0.html) + [Article: New features in version 3.0](https://www.cnblogs.com/savorboard/p/cap-3-0.html) [Article: New features in version 2.6](https://www.cnblogs.com/savorboard/p/cap-2-6.html) diff --git a/docs/content/user-guide/en/getting-started/quick-start.md b/docs/content/user-guide/en/getting-started/quick-start.md index eb620f0..3a67edc 100644 --- a/docs/content/user-guide/en/getting-started/quick-start.md +++ b/docs/content/user-guide/en/getting-started/quick-start.md @@ -36,7 +36,7 @@ public void ConfigureServices(IServiceCollection services) public class PublishController : Controller { [Route("~/send")] - public IActionResult SendMessage([FromService]ICapPublisher capBus) + public IActionResult SendMessage([FromServices]ICapPublisher capBus) { capBus.Publish("test.show.time", DateTime.Now); @@ -45,6 +45,19 @@ public class PublishController : Controller } ``` +### Publish with extra header + +```c# +var header = new Dictionary() +{ + ["my.header.first"] = "first", + ["my.header.second"] = "second" +}; + +capBus.Publish("test.show.time", DateTime.Now, header); + +``` + ## Process Message ```C# @@ -59,6 +72,19 @@ public class ConsumerController : Controller } ``` +### Process with extra header + +```c# +[CapSubscribe("test.show.time")] +public void ReceiveMessage(DateTime time, [FromCap]CapHeader header) +{ + Console.WriteLine("message time is:" + time); + Console.WriteLine("message firset header :" + header["my.header.first"]); + Console.WriteLine("message second header :" + header["my.header.second"]); +} + +``` + ## Summary One of the most powerful advantages of asynchronous messaging over direct integrated message queues is reliability, where failures in one part of the system do not propagate or cause the entire system to crash. Messages are stored inside the CAP to ensure the reliability of the message, and strategies such as retry are used to achieve the final consistency of data between services. \ No newline at end of file diff --git a/docs/content/user-guide/en/monitoring/dashboard.md b/docs/content/user-guide/en/monitoring/dashboard.md index fa0fc61..b198d64 100644 --- a/docs/content/user-guide/en/monitoring/dashboard.md +++ b/docs/content/user-guide/en/monitoring/dashboard.md @@ -32,39 +32,66 @@ You can change the path of the Dashboard by modifying this configuration option. This configuration option is used to configure the Dashboard front end to get the polling time of the status interface (/stats). -* Authorization +* UseAuth -This configuration option is used to configure the authorization filter when accessing the Dashboard. The default filter allows LAN access. When your application wants to provide external network access, you can customize authentication rules by setting this configuration. See the next section for details. +> Default:false -### Custom authentication +Enable authentication on dashboard request. -Dashboard authentication can be customized by implementing the `IDashboardAuthorizationFilter` interface. +* DefaultAuthenticationScheme -The following is a sample code that determines if access is allowed by reading the accesskey from the url request parameter. +Default scheme used for authentication. If no scheme is set, the DefaultScheme set up in AddAuthentication will be used. + +* UseChallengeOnAuth + +> Default:false + +Enable authentication challenge on dashboard request. + +* DefaultChallengeScheme + +Default scheme used for authentication challenge. If no scheme is set, the DefaultChallengeScheme set up in AddAuthentication will be used. + +### Custom authentication + +From version 5.1.0, Dashboard authorization uses ASP.NET Core style by default and no longer provides custom authorization filters. + +During Dashabord authentication, the value will be taken from `HttpContext.User?.Identity?.IsAuthenticated`. If it is not available, the authentication will fail and the `DefaultChallengeScheme` will be called (if configured). + +You can view the usage details in the sample project `Sample.Dashboard.Auth`. ```C# -public class TestAuthorizationFilter : IDashboardAuthorizationFilter -{ - public bool Authorize(DashboardContext context) +services + .AddAuthorization() + .AddAuthentication(options => { - if(context.Request.GetQuery("accesskey")=="xxxxxx"){ - return true; - } - return false; - } -} + options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; + }) + .AddCookie() + .AddOpenIdConnect(options => + { + options.Authority = "https://demo.identityserver.io/"; + options.ClientId = "interactive.confidential"; + options.ClientSecret = "secret"; + options.ResponseType = "code"; + options.UsePkce = true; + + options.Scope.Clear(); + options.Scope.Add("openid"); + options.Scope.Add("profile"); + }) ``` -Then configure this filter when registration Dashboard. +configuration: ```C# -services.AddCap(x => +services.AddCap(cap => { - //... - - // Register Dashboard - x.UseDashboard(opt => { - opt.Authorization = new[] {new TestAuthorizationFilter()}; + cap.UseDashboard(d => + { + d.UseChallengeOnAuth = true; + d.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }); -}); +} ``` diff --git a/docs/content/user-guide/en/monitoring/health-checks.md b/docs/content/user-guide/en/monitoring/health-checks.md deleted file mode 100644 index 29f5938..0000000 --- a/docs/content/user-guide/en/monitoring/health-checks.md +++ /dev/null @@ -1,3 +0,0 @@ -# Health Checks - -TODO \ No newline at end of file diff --git a/docs/content/user-guide/en/samples/faq.md b/docs/content/user-guide/en/samples/faq.md index 4fef6e6..4bf854b 100644 --- a/docs/content/user-guide/en/samples/faq.md +++ b/docs/content/user-guide/en/samples/faq.md @@ -2,19 +2,19 @@ !!! faq "Any IM group(e.g Tencent QQ group) to learn and chat about CAP?" - None for that. Better than wasting much time in IM group, I hope developers could be capable of independent thinking more, and solve problems yourselves with referenced documents, even create issues or send emails when errors are remaining present. + None of that. Better than wasting much time in IM group, I hope developers could be capable of independent thinking more, and solve problems yourselves with referenced documents, even create issues or send emails when errors are remaining present. -!!! faq "Does it require certain different databases, one each for productor and resumer in CAP?" +!!! faq "Does it require different databases, one each for producer and consumer in CAP?" - Not required differences necessary, a given advice is that using a special database for each program. + No difference necessary, a recommendation is to use a dedicated database for each program. Otherwise, look at Q&A below. -!!! faq "How to use the same database for different applications?" - - defining a prefix name of table in `ConfigureServices` method。 +!!! faq "How to use the same database for different applications? (Only for MySQL)" + + Define a table prefix name in `ConfigureServices` method. - codes exsample: + Code example: ```c# public void ConfigureServices(IServiceCollection services) @@ -31,14 +31,14 @@ } ``` -!!! faq "Can CAP not use the database as event storage? I just want to sent the message" +!!! faq "Can CAP not use the database as event storage? I just want to send the message" Not yet. - The purpose of CAP is that ensure consistency principle right in microservice or SOA architechtrues. The solution is based on ACID features of database, there is no sense about a single client wapper of message queue without database. + The purpose of CAP is that ensure consistency principle right in microservice or SOA architectures. The solution is based on ACID features of database, there is no sense about a single client wapper of message queue without database. !!! faq "If the consumer is abnormal, can I roll back the database executed sql that the producer has executed?" Can't roll back, CAP is the ultimate consistency solution. - You can imagine your scenario is to call a third party payment. If you are doing a third-party payment operation, after calling Alipay's interface successfully, and your own code is wrong, will Alipay roll back? If you don't roll back, what should you do? The same is true here. \ No newline at end of file + You can imagine your scenario is to call a third party payment. If you are doing a third-party payment operation, after calling Alipay's interface successfully, and your own code is wrong, will Alipay roll back? If you don't roll back, what should you do? The same is true here. diff --git a/docs/content/user-guide/en/storage/general.md b/docs/content/user-guide/en/storage/general.md index e178f69..f95b474 100644 --- a/docs/content/user-guide/en/storage/general.md +++ b/docs/content/user-guide/en/storage/general.md @@ -67,4 +67,15 @@ Timestamp | Message created time | string Content | Message content | string CallbackName | Consumer callback topic name | string -The `Id` field is generate using the mongo [objectid algorithm](https://www.mongodb.com/blog/post/generating-globally-unique-identifiers-for-use-with-mongodb). \ No newline at end of file +The `Id` field is generate using the mongo [objectid algorithm](https://www.mongodb.com/blog/post/generating-globally-unique-identifiers-for-use-with-mongodb). + + +## Community-supported extensions + +Thanks to the community for supporting CAP, the following is the implementation of community-supported storage + +* SQLite ([@colinin](https://github.com/colinin)) :https://github.com/colinin/DotNetCore.CAP.Sqlite + +* LiteDB ([@maikebing](https://github.com/maikebing)) :https://github.com/maikebing/CAP.Extensions + +* SQLite & Oracle ([@cocosip](https://github.com/cocosip)) :https://github.com/cocosip/CAP-Extensions diff --git a/docs/content/user-guide/en/storage/sqlserver.md b/docs/content/user-guide/en/storage/sqlserver.md index c66a82a..f860d90 100644 --- a/docs/content/user-guide/en/storage/sqlserver.md +++ b/docs/content/user-guide/en/storage/sqlserver.md @@ -24,7 +24,7 @@ public void ConfigureServices(IServiceCollection services) services.AddCap(x => { - x.UsePostgreSql(opt=>{ + x.UseSqlServer(opt=>{ //SqlServerOptions }); // x.UseXXX ... diff --git a/docs/content/user-guide/en/transport/azure-service-bus.md b/docs/content/user-guide/en/transport/azure-service-bus.md index 173e24d..9c1e7b3 100644 --- a/docs/content/user-guide/en/transport/azure-service-bus.md +++ b/docs/content/user-guide/en/transport/azure-service-bus.md @@ -41,5 +41,24 @@ The AzureServiceBus configuration options provided directly by the CAP: NAME | DESCRIPTION | TYPE | DEFAULT :---|:---|---|:--- ConnectionString | Endpoint address | string | +EnableSessions | Enable [Service bus sessions](https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-sessions) | bool | false TopicPath | Topic entity path | string | cap -ManagementTokenProvider | Token provider | ITokenProvider | null \ No newline at end of file +ManagementTokenProvider | Token provider | ITokenProvider | null + +#### Sessions + +When sessions are enabled (see `EnableSessions` option above), every message sent will have a session id. To control the session id, include +an extra header with name `AzureServiceBusHeaders.SessionId` when publishing events: + +```csharp +ICapPublisher capBus = ...; +string yourEventName = ...; +YourEventType yourEvent = ...; + +Dictionary extraHeaders = new Dictionary(); +extraHeaders.Add(AzureServiceBusHeaders.SessionId, ); + +capBus.Publish(yourEventName, yourEvent, extraHeaders); +``` + +If no session id header is present, the message id will be used as the session id. diff --git a/docs/content/user-guide/en/transport/general.md b/docs/content/user-guide/en/transport/general.md index 4e764ba..a34071d 100644 --- a/docs/content/user-guide/en/transport/general.md +++ b/docs/content/user-guide/en/transport/general.md @@ -10,7 +10,9 @@ CAP supports several transport methods: * [Kafka](kafka.md) * [Azure Service Bus](azure-service-bus.md) * [Amazon SQS](aws-sqs.md) +* [NATS](nats.md) * [In-Memory Queue](in-memory-queue.md) +* [Redis Streams](redis-streams.md) ## How to select a transport @@ -28,3 +30,15 @@ CAP supports several transport methods: >`Kafka` vs `RabbitMQ` : > https://stackoverflow.com/questions/42151544/is-there-any-reason-to-use-rabbitmq-over-kafka +## Community-supported extensions + +Thanks to the community for supporting CAP, the following is the implementation of community-supported transport + +* ActiveMQ (@[Lukas Zhang](https://github.com/lukazh/Lukaz.CAP.ActiveMQ)): https://github.com/lukazh + +* RedisMQ ([@木木](https://github.com/difudotnet)) https://github.com/difudotnet/CAP.RedisMQ.Extensions + +* ZeroMQ ([@maikebing](https://github.com/maikebing)): https://github.com/maikebing/CAP.Extensions + + + diff --git a/docs/content/user-guide/en/transport/kafka.md b/docs/content/user-guide/en/transport/kafka.md index 8ea08bb..4aae3c6 100644 --- a/docs/content/user-guide/en/transport/kafka.md +++ b/docs/content/user-guide/en/transport/kafka.md @@ -40,6 +40,42 @@ NAME | DESCRIPTION | TYPE | DEFAULT :---|:---|---|:--- Servers | Broker server address | string | ConnectionPoolSize | connection pool size | int | 10 +CustomHeaders | Custom subscribe headers | Func<> | N/A + +#### CustomHeaders Options + +When the message sent from a heterogeneous system, because of the CAP needs to define additional headers, so an exception will occur at this time. By providing this parameter to set the custom headersn to make the subscriber works. + +You can find the description of [Header Information](../cap/messaging#heterogeneous-system-integration) here. + +Sometimes, if you want to get additional context information from Broker, you can also add it through this option. For example, add information such as Offset or Partition. + +Example: + +```C# +x.UseKafka(opt => +{ + //... + + opt.CustomHeaders = kafkaResult => new List> + { + new KeyValuePair("my.kafka.offset", kafkaResult.Offset.ToString()), + new KeyValuePair("my.kafka.partition", kafkaResult.Partition.ToString()) + }; +}); +``` + +Then you can get the header you added by this way: + +```C# +[CapSubscribe("sample.kafka.postgrsql")] +public void HeadersTest(DateTime value, [FromCap]CapHeader header) +{ + var offset = header["my.kafka.offset"]; + var partition = header["my.kafka.partition"]; +} +``` + #### Kafka MainConfig Options diff --git a/docs/content/user-guide/en/transport/nats.md b/docs/content/user-guide/en/transport/nats.md new file mode 100644 index 0000000..7f4c552 --- /dev/null +++ b/docs/content/user-guide/en/transport/nats.md @@ -0,0 +1,60 @@ +# NATS + +[NATS](https://nats.io/) is a simple, secure and performant communications system for digital systems, services and devices. NATS is part of the Cloud Native Computing Foundation (CNCF). + +!!! warning + We currently implement NATS provider based on Request/Response mode, and we plan to replace it with JetStream in future version. + see https://github.com/dotnetcore/CAP/issues/983 for more information. + +## Configuration + +To use NATS transporter, you need to install the following package from NuGet: + +```powershell + +PM> Install-Package DotNetCore.CAP.NATS + +``` + +Then you can add configuration items to the `ConfigureServices` method of `Startup.cs`. + +```csharp + +public void ConfigureServices(IServiceCollection services) +{ + services.AddCap(capOptions => + { + capOptions.UseNATS(natsOptions=>{ + //NATS Options + }); + }); +} + +``` + +#### NATS Options + +NATS configuration parameters provided directly by the CAP: + +NAME | DESCRIPTION | TYPE | DEFAULT +:---|:---|---|:--- +Options | NATS client configuration | Options | Options +Servers | Server url/urls used to connect to the NATs server. | string | NULL +ConnectionPoolSize | number of connections pool | uint | 10 + +#### NATS ConfigurationOptions + +If you need **more** native NATS related configuration options, you can set them in the `Options` option: + +```csharp +services.AddCap(capOptions => +{ + capOptions.UseNATS(natsOptions=> + { + // NATS options. + natsOptions.Options.Url=""; + }); +}); +``` + +`Options` is a NATS.Client ConfigurationOptions , you can find more details through this [link](http://nats-io.github.io/nats.net/class_n_a_t_s_1_1_client_1_1_options.html) diff --git a/docs/content/user-guide/en/transport/rabbitmq.md b/docs/content/user-guide/en/transport/rabbitmq.md index 658a751..fbd2573 100644 --- a/docs/content/user-guide/en/transport/rabbitmq.md +++ b/docs/content/user-guide/en/transport/rabbitmq.md @@ -44,8 +44,10 @@ UserName | Broker user name | string | guest Password | Broker password | string | guest VirtualHost | Broker virtual host | string | / Port | Port | int | -1 -TopicExchangeName | Default exchange name of cap created | string | cap.default.topic -QueueMessageExpires | Message expries after to delete, in milliseconds | int | (10 days) milliseconds +ExchangeName | Default exchange name | string | cap.default.topic +QueueArguments | Extra queue `x-arguments` | QueueArgumentsOptions | N/A +ConnectionFactoryOptions | RabbitMQClient native connection options | ConnectionFactory | N/A +CustomHeaders | Custom subscribe headers | Func>> | N/A #### ConnectionFactory Options @@ -66,6 +68,25 @@ services.AddCap(x => ``` +#### CustomHeaders Options + +When the message sent from the RabbitMQ management console or a heterogeneous system, because of the CAP needs to define additional headers, so an exception will occur at this time. By providing this parameter to set the custom headersn to make the subscriber works. + +You can find the description of [Header Information](../cap/messaging#heterogeneous-system-integration) here. + +Example: + +```cs +x.UseRabbitMQ(aa => +{ + aa.CustomHeaders = e => new List> + { + new KeyValuePair(Headers.MessageId, SnowflakeId.Default().NextId().ToString()), + new KeyValuePair(Headers.MessageName, e.RoutingKey), + }; +}); +``` + #### How to connect cluster using comma split connection string, like this: diff --git a/docs/content/user-guide/en/transport/redis-streams.md b/docs/content/user-guide/en/transport/redis-streams.md new file mode 100644 index 0000000..315e592 --- /dev/null +++ b/docs/content/user-guide/en/transport/redis-streams.md @@ -0,0 +1,59 @@ +# Redis Streams + +[Redis](https://redis.io/) is an open source (BSD licensed), in-memory data structure store, used as a database, cache, and message broker. + +[Redis Stream](https://redis.io/topics/streams-intro) is a new data type introduced with Redis 5.0, which models a log data structure in a more abstract way with an append only data structure. + +Redis Streams can be used in CAP as a message transporter. + +## Configuration + +To use Redis Streams transporter, you need to install the following package from NuGet: + +```powershell +PM> Install-Package DotNetCore.CAP.RedisStreams + +``` + +Then you can add configuration items to the `ConfigureServices` method of `Startup.cs`. + +```csharp + +public void ConfigureServices(IServiceCollection services) +{ + services.AddCap(capOptions => + { + capOptions.UseRedis(redisOptions=>{ + //redisOptions + }); + }); +} + +``` + +#### Redis Streams Options + +Redis configuration parameters provided directly by the CAP: + +NAME | DESCRIPTION | TYPE | DEFAULT +:---|:---|---|:--- +Configuration | redis connection configuration (StackExchange.Redis) | ConfigurationOptions | ConfigurationOptions +StreamEntriesCount | number of entries returned from a stream while reading | uint | 10 +ConnectionPoolSize | number of connections pool | uint | 10 + +#### Redis ConfigurationOptions + +If you need **more** native Redis related configuration options, you can set them in the `Configuration` option: + +```csharp +services.AddCap(capOptions => +{ + capOptions.UseRedis(redisOptions=> + { + // redis options. + redisOptions.Configuration.EndPoints.Add(IPAddress.Loopback, 0); + }); +}); +``` + +`Configuration` is a StackExchange.Redis ConfigurationOptions , you can find more details through this [link](https://stackexchange.github.io/StackExchange.Redis/Configuration) diff --git a/docs/content/user-guide/zh/cap/configuration.md b/docs/content/user-guide/zh/cap/configuration.md index 8f70d03..b4bd7c1 100644 --- a/docs/content/user-guide/zh/cap/configuration.md +++ b/docs/content/user-guide/zh/cap/configuration.md @@ -30,15 +30,31 @@ services.AddCap(config => 在 `AddCap` 中 `CapOptions` 对象是用来存储配置相关信息,默认情况下它们都具有一些默认值,有些时候你可能需要自定义。 -#### DefaultGroup +#### DefaultGroupName 默认值:cap.queue.{程序集名称} 默认的消费者组的名字,在不同的 Transports 中对应不同的名字,可以通过自定义此值来自定义不同 Transports 中的名字,以便于查看。 -> 在 RabbitMQ 中映射到 [Queue Names](https://www.rabbitmq.com/queues.html#names)。 -> 在 Apache Kafka 中映射到 [Consumer Group Id](http://kafka.apache.org/documentation/#group.id)。 -> 在 Azure Service Bus 中映射到 Subscription Name。 +!!! info "Mapping" + 在 RabbitMQ 中映射到 [Queue Names](https://www.rabbitmq.com/queues.html#names)。 + 在 Apache Kafka 中映射到 [Consumer Group Id](http://kafka.apache.org/documentation/#group.id)。 + 在 Azure Service Bus 中映射到 Subscription Name。 + 在 NATS 中映射到 [Queue Group Name](https://docs.nats.io/nats-concepts/queue). + 在 Redis Streams 中映射到 [Consumer Group](https://redis.io/topics/streams-intro#creating-a-consumer-group). + + +#### GroupNamePrefix + +默认值:Null + +为订阅 Group 统一添加前缀。 https://github.com/dotnetcore/CAP/pull/780 + +#### TopicNamePrefix + +默认值: Null + +为 Topic 统一添加前缀。 https://github.com/dotnetcore/CAP/pull/780 #### Version @@ -76,6 +92,12 @@ services.AddCap(config => 消费者线程并行处理消息的线程数,当这个值大于1时,将不能保证消息执行的顺序。 +#### CollectorCleaningInterval + +默认值:300 秒 + +收集器删除已经过期消息的时间间隔。 + #### FailedRetryCount 默认值:50 @@ -86,12 +108,7 @@ services.AddCap(config => 默认值:NULL -类型:`Action` - -> -T1 : Message Type -T2 : Message Name -T3 : Message Content +类型:`Action` 重试阈值的失败回调。当重试达到 FailedRetryCount 设置的值的时候,将调用此 Action 回调,你可以通过指定此回调来接收失败达到最大的通知,以做出人工介入。例如发送邮件或者短信。 diff --git a/docs/content/user-guide/zh/cap/filter.md b/docs/content/user-guide/zh/cap/filter.md new file mode 100644 index 0000000..b355107 --- /dev/null +++ b/docs/content/user-guide/zh/cap/filter.md @@ -0,0 +1,53 @@ +# 过滤器 + +在 5.1.0 版本中,我们支持了在订阅者中添加过滤器。在过去,我们通过对第三方 AOP 组件提供支持来做到这一点,例如我们写了一篇[博客](https://www.cnblogs.com/savorboard/p/cap-castle.html) 来描述如何在 CAP 5.0 版本中使用 Castle 来对订阅方法进行拦截,但了这种方式存在一些缺点,例如无法方便的在代理类中进行构造函数注入以及方法需要设定为 virtual 另外还有拦截器生命周期控制等问题。 + +所以我们引入了对订阅者过滤器的支持,以使在某些场景(如事务处理,日志记录等)中变得容易。 + +## 自定义过滤器 + +### 添加过滤器 + +创建一个过滤器类,并继承 `SubscribeFilter` 抽象类。 + +```C# +public class MyCapFilter: SubscribeFilter +{ + public override void OnSubscribeExecuting(ExecutingContext context) + { + // 订阅方法执行前 + } + + public override void OnSubscribeExecuted(ExecutedContext context) + { + // 订阅方法执行后 + } + + public override void OnSubscribeException(ExceptionContext context) + { + // 订阅方法执行异常 + } +} +``` + +在一些场景中,如果想终止订阅者方法执行,可以在 `OnSubscribeExecuting` 中抛出异常,并且在 `OnSubscribeException` 中选择忽略该异常。 + +通过在 `ExceptionContext` 中设置 `context.ExceptionHandled = true` 来忽略异常。 + +```C# +public override void OnSubscribeException(ExceptionContext context) +{ + context.ExceptionHandled = true; +} +``` + +### 配置过滤器 + +```C# +services.AddCap(opt => +{ + // *** +}.AddSubscribeFilter(); +``` + +目前, 我们还不支持同时添加多个过滤器。 diff --git a/docs/content/user-guide/zh/cap/messaging.md b/docs/content/user-guide/zh/cap/messaging.md index bd853a6..254559d 100644 --- a/docs/content/user-guide/zh/cap/messaging.md +++ b/docs/content/user-guide/zh/cap/messaging.md @@ -6,6 +6,54 @@ 你可以阅读 [quick-start](../getting-started/quick-start.md#_3) 来学习如何发送和处理消息。 +## 补偿事务 + +[Compensating transaction](https://en.wikipedia.org/wiki/Compensating_transaction) + +某些情况下,消费者需要返回值以告诉发布者执行结果,以便于发布者实施一些动作,通常情况下这属于补偿范围。 + +你可以在消费者执行的代码中通过重新发布一个新消息来通知上游,CAP 提供了一种简单的方式来做到这一点。 你可以在发送的时候指定 `callbackName` 来得到消费者的执行结果,通常这仅适用于点对点的消费。以下是一个示例。 + +例如,在一个电商程序中,订单初始状态为 pending,当商品数量成功扣除时将状态标记为 succeeded ,否则为 failed。 + +```C# +// ============= Publisher ================= + +_capBus.Publish("place.order.qty.deducted", + contentObj: new { OrderId = 1234, ProductId = 23255, Qty = 1 }, + callbackName: "place.order.mark.status"); + +// publisher using `callbackName` to subscribe consumer result + +[CapSubscribe("place.order.mark.status")] +public void MarkOrderStatus(JsonElement param) +{ + var orderId = param.GetProperty("OrderId").GetInt32(); + var isSuccess = param.GetProperty("IsSuccess").GetBoolean(); + + if(isSuccess){ + // mark order status to succeeded + } + else{ + // mark order status to failed + } +} + +// ============= Consumer =================== + +[CapSubscribe("place.order.qty.deducted")] +public object DeductProductQty(JsonElement param) +{ + var orderId = param.GetProperty("OrderId").GetInt32(); + var productId = param.GetProperty("ProductId").GetInt32(); + var qty = param.GetProperty("Qty").GetInt32(); + + //business logic + + return new { OrderId = orderId, IsSuccess = true }; +} +``` + ## 异构系统集成 在 3.0+ 版本中,我们对消息结构进行了重构,我们利用了消息队列中消息协议中的 Header 来传输一些额外信息,以便于在 Body 中我们可以做到不需要修改或包装使用者的原始消息数据格式和内容进行发送。 @@ -45,7 +93,6 @@ channel.basicPublish(exchangeName, routingKey, ``` - ## 消息调度 CAP 接收到消息之后会将消息发送到 Transport, 由 Transport 进行运输。 @@ -66,7 +113,7 @@ CAP 接收到消息之后会将消息进行 Persistent(持久化), 有关 在消息发送过程中,当出现 Broker 宕机或者连接失败的情况亦或者出现异常的情况下,这个时候 CAP 会对发送的重试,第一次重试次数为 3,4分钟后以后每分钟重试一次,进行次数 +1,当总次数达到50次后,CAP将不对其进行重试。 -你可以在 CapOptions 中设置FailedRetryCount来调整默认重试的总次数。 +你可以在 CapOptions 中设置 [FailedRetryCount](../configuration#failedretrycount) 来调整默认重试的总次数。 当失败总次数达到默认失败总次数后,就不会进行重试了,你可以在 Dashboard 中查看消息失败的原因,然后进行人工重试处理。 @@ -78,4 +125,5 @@ CAP 接收到消息之后会将消息进行 Persistent(持久化), 有关 数据库消息表中具有一个 ExpiresAt 字段表示消息的过期时间,当消息发送成功或者消费成功后,CAP会将消息状态为 Successed 的 ExpiresAt 设置为 1天 后过期,会将消息状态为 Failed 的 ExpiresAt 设置为 15天 后过期。 -CAP 默认情况下会每隔一个小时将消息表的数据进行清理删除,避免数据量过多导致性能的降低。清理规则为 ExpiresAt 不为空并且小于当前时间的数据。 也就是说状态为Failed的消息(正常情况他们已经被重试了 50 次),如果你15天没有人工介入处理,同样会被清理掉。 +CAP 默认情况下会每隔**5分钟**将消息表的数据进行清理删除,避免数据量过多导致性能的降低。清理规则为 ExpiresAt 不为空并且小于当前时间的数据。 也就是说状态为Failed的消息(正常情况他们已经被重试了 50 次),如果你15天没有人工介入处理,同样会被清理掉。你可以通过 [CollectorCleaningInterval](../configuration#collectorcleaninginterval) 配置项来自定义间隔时间。 + diff --git a/docs/content/user-guide/zh/cap/sagas.md b/docs/content/user-guide/zh/cap/sagas.md deleted file mode 100644 index 50f4402..0000000 --- a/docs/content/user-guide/zh/cap/sagas.md +++ /dev/null @@ -1,7 +0,0 @@ -# Sagas - -Sagas (also known in the literature as "process managers") are stateful services. You can think of them as state machines whose transitions are driven by messages. - -## Sagas on CAP - -TODO \ No newline at end of file diff --git a/docs/content/user-guide/zh/getting-started/introduction.md b/docs/content/user-guide/zh/getting-started/introduction.md index 68c49ba..c87d1cf 100644 --- a/docs/content/user-guide/zh/getting-started/introduction.md +++ b/docs/content/user-guide/zh/getting-started/introduction.md @@ -25,6 +25,8 @@ CAP 采用模块化设计,具有高度的可扩展性。你有许多选项可 [Article: CAP 介绍及使用](http://www.cnblogs.com/savorboard/p/cap.html) +[Article: CAP 5.0 版本中的新特性](https://www.cnblogs.com/savorboard/p/cap-5-0.html) + [Article: CAP 3.0 版本中的新特性](https://www.cnblogs.com/savorboard/p/cap-3-0.html) [Article: CAP 2.6 版本中的新特性](https://www.cnblogs.com/savorboard/p/cap-2-6.html) diff --git a/docs/content/user-guide/zh/getting-started/quick-start.md b/docs/content/user-guide/zh/getting-started/quick-start.md index 47f80c0..ec8eae0 100644 --- a/docs/content/user-guide/zh/getting-started/quick-start.md +++ b/docs/content/user-guide/zh/getting-started/quick-start.md @@ -36,7 +36,7 @@ public void ConfigureServices(IServiceCollection services) public class PublishController : Controller { [Route("~/send")] - public IActionResult SendMessage([FromService]ICapPublisher capBus) + public IActionResult SendMessage([FromServices]ICapPublisher capBus) { capBus.Publish("test.show.time", DateTime.Now); diff --git a/docs/content/user-guide/zh/monitoring/dashboard.md b/docs/content/user-guide/zh/monitoring/dashboard.md index 3e252b8..b168a33 100644 --- a/docs/content/user-guide/zh/monitoring/dashboard.md +++ b/docs/content/user-guide/zh/monitoring/dashboard.md @@ -26,6 +26,12 @@ services.AddCap(x => ### Dashboard 配置项 +* PathBase + +默认值:N/A + +当位于代理后时,通过配置此参数可以指定代理请求前缀。 + * PathMatch 默认值:'/cap' @@ -38,39 +44,67 @@ services.AddCap(x => 此配置项用来配置Dashboard 前端 获取状态接口(/stats)的轮询时间 -* Authorization +* UseAuth + +默认值:false + +指定是否开启授权 + +* DefaultAuthenticationScheme + +授权默认使用的 Scheme + +* UseChallengeOnAuth + +默认值:false + +授权是否启用 Challenge + +* DefaultChallengeScheme + +Challenge 默认使用的 Scheme -此配置项用来配置访问 Dashboard 时的授权过滤器,默认过滤器允许局域网访问,当你的应用想提供外网访问时候,可以通过设置此配置来自定义认证规则。详细参看下一节 ### 自定义认证 + +自 5.1.0 开始,CAP Dashboard 授权默认使用 ASP.NET Core 的方式,不再提供自定义授权过滤器。 -通过实现 `IDashboardAuthorizationFilter` 接口可以自定义Dashboard认证。 +在 Dashabord 认证时,会从 HttpContext.User?.Identity?.IsAuthenticated 中取值,如果取不到则认证失败,并调用 Challenge Scheme(如进行配置)。 -以下是一个示例代码,通过从url请求参数中读取 accesskey 判断是否允许访问。 +你可以在 Sample.Dashboard.Auth 这个示例项目中查看使用细节。 ```C# -public class TestAuthorizationFilter : IDashboardAuthorizationFilter -{ - public bool Authorize(DashboardContext context) +services + .AddAuthorization() + .AddAuthentication(options => { - if(context.Request.GetQuery("accesskey")=="xxxxxx"){ - return true; - } - return false; - } -} + options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; + }) + .AddCookie() + .AddOpenIdConnect(options => + { + options.Authority = "https://demo.identityserver.io/"; + options.ClientId = "interactive.confidential"; + options.ClientSecret = "secret"; + options.ResponseType = "code"; + options.UsePkce = true; + + options.Scope.Clear(); + options.Scope.Add("openid"); + options.Scope.Add("profile"); + }) ``` -然后在修改注册 Dashboard 时候配置此过滤对象。 +配置 ```C# -services.AddCap(x => +services.AddCap(cap => { - //... - - // Register Dashboard - x.UseDashboard(opt => { - opt.Authorization = new[] {new TestAuthorizationFilter()}; + cap.UseDashboard(d => + { + d.UseChallengeOnAuth = true; + d.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }); -}); +} ``` diff --git a/docs/content/user-guide/zh/monitoring/health-checks.md b/docs/content/user-guide/zh/monitoring/health-checks.md deleted file mode 100644 index 4f3601d..0000000 --- a/docs/content/user-guide/zh/monitoring/health-checks.md +++ /dev/null @@ -1,3 +0,0 @@ -# 健康检查 - -TODO \ No newline at end of file diff --git a/docs/content/user-guide/zh/samples/castle.dynamicproxy.md b/docs/content/user-guide/zh/samples/castle.dynamicproxy.md new file mode 100644 index 0000000..2ab5e50 --- /dev/null +++ b/docs/content/user-guide/zh/samples/castle.dynamicproxy.md @@ -0,0 +1,123 @@ +# 和 Castle DynamicProxy 集成 + +Castle DynamicProxy 是一个用于在运行时动态生成轻量级.NET代理的库。代理对象允许在不修改类代码的情况下截取对对象成员的调用。可以代理类和接口,但是只能拦截虚拟成员。 + +Castle.DynamicProxy 可以帮助你方便的创建代理对象,代理对象可以帮助构建灵活的应用程序体系结构,因为它允许将功能透明地添加到代码中,而无需对其进行修改。例如,可以代理一个类来添加日志记录或安全检查,而无需使代码知道已添加此功能。 + +下面可以看到如何在 CAP 中集成使用 Castle.DynamicProxy。 + + +## 1、安装 NuGet 包 + +在 集成了 CAP 的项目中安装包,有关如何集成 CAP 的文档请看[这里](https://cap.dotnetcore.xyz/)。 + +注意,`Castle.DynamicProxy` 这个包已经被废弃,请使用最新的 `Castle.Core` 包。 + +```xml + +``` + +## 2、创建一个 Castle 切面拦截器 + +可以在这里 [dynamicproxy.md](https://github.com/castleproject/Core/blob/master/docs/dynamicproxy.md) 找到相关的文档。 + +下面为示例代码,继承 Castle 提供的 `IInterceptor` 接口即可: + +``` +[Serializable] +public class MyInterceptor : IInterceptor +{ + public void Intercept(IInvocation invocation) + { + Console.WriteLine("Before target call"); + try + { + invocation.Proceed(); + } + catch (Exception) + { + Console.WriteLine("Target threw an exception!"); + throw; + } + finally + { + Console.WriteLine("After target call"); + } + } +} + +``` + +拦截器此处命名为 `MyInterceptor`,你可以在其中处理你的业务逻辑,比如添加日志或其他的一些行为。 + +## 3、创建 IServiceCollection 的扩展类 + +为 `IServiceCollection` 创建扩展,方面后续调用。 + +```csharp +using Castle.DynamicProxy; + +public static class ServicesExtensions +{ + public static void AddProxiedSingleton(this IServiceCollection services) + where TImplementation : class + { + services.AddSingleton(serviceProvider => + { + var proxyGenerator = serviceProvider.GetRequiredService(); + var interceptors = serviceProvider.GetServices().ToArray(); + return proxyGenerator.CreateClassProxy(interceptors); + }); + } +} +``` + +此处我创建了一个 Singleton 声明周期的扩展方法,建议所有 CAP 的订阅者都创建为 Singleton 即可,因为在 CAP 内部实际执行的时候也会创建一个 scope 来执行,所以无需担心资源释放问题。 + + +## 4、创建 CAP 订阅服务 + +创建一个 CAP 订阅类,注意不能放在 Controller 中了。 + +**注意:方法需要为虚方法 virtual,才能被 Castle 重写,别搞忘了加!!!** + +```cs +public class CapSubscribeService: ICapSubscribe +{ + [CapSubscribe("sample.rabbitmq.mysql")] + public virtual void Subscriber(DateTime p) + { + Console.WriteLine($@"{DateTime.Now} Subscriber invoked, Info: {p}"); + } +} +``` + +## 5、在 Startup 中集成 + +```cs +public void ConfigureServices(IServiceCollection services) +{ + // 添加 Castle 的代理生成器 + services.AddSingleton(new ProxyGenerator()); + + // 添加第2步的自定义的拦截类,声明周期为 + services.AddSingleton(); + + // 此处为上面的扩展方法, 添加 CAP 订阅 Service + services.AddProxiedSingleton(); + + services.AddCap(x => + { + x.UseMySql(""); + x.UseRabbitMQ(""); + x.UseDashboard(); + }); + + // ... +} +``` + +以上就完成了所有的集成工作,可以开始进行测试了,有问题欢迎到 [Github issue](https://github.com/dotnetcore/CAP/issues) 反馈。 + + +**注意: CAP 需要使用 5.0 + 版本,目前(2021年1月6日)只有 preview 版本。** diff --git a/docs/content/user-guide/zh/samples/faq.md b/docs/content/user-guide/zh/samples/faq.md index 1715ca2..4bf854b 100644 --- a/docs/content/user-guide/zh/samples/faq.md +++ b/docs/content/user-guide/zh/samples/faq.md @@ -2,19 +2,19 @@ !!! faq "Any IM group(e.g Tencent QQ group) to learn and chat about CAP?" - None for that. Better than wasting much time in IM group, I hope developers could be capable of independent thinking more, and solve problems yourselves with referenced documents, even create issues or send emails when errors are remaining present. + None of that. Better than wasting much time in IM group, I hope developers could be capable of independent thinking more, and solve problems yourselves with referenced documents, even create issues or send emails when errors are remaining present. -!!! faq "Does it require certain different databases, one each for productor and resumer in CAP?" +!!! faq "Does it require different databases, one each for producer and consumer in CAP?" - Not requird differences necessary, a given advice is that using a special database for each program. + No difference necessary, a recommendation is to use a dedicated database for each program. Otherwise, look at Q&A below. -!!! faq "How to use the same database for different applications?" - - defining a prefix name of table in `ConfigureServices` method。 +!!! faq "How to use the same database for different applications? (Only for MySQL)" + + Define a table prefix name in `ConfigureServices` method. - codes exsample: + Code example: ```c# public void ConfigureServices(IServiceCollection services) @@ -31,14 +31,14 @@ } ``` -!!! faq "Can CAP not use the database as event storage? I just want to sent the message" +!!! faq "Can CAP not use the database as event storage? I just want to send the message" Not yet. - The purpose of CAP is that ensure consistency principle right in microservice or SOA architechtrues. The solution is based on ACID features of database, there is no sense about a single client wapper of message queue without database. + The purpose of CAP is that ensure consistency principle right in microservice or SOA architectures. The solution is based on ACID features of database, there is no sense about a single client wapper of message queue without database. !!! faq "If the consumer is abnormal, can I roll back the database executed sql that the producer has executed?" Can't roll back, CAP is the ultimate consistency solution. - You can imagine your scenario is to call a third party payment. If you are doing a third-party payment operation, after calling Alipay's interface successfully, and your own code is wrong, will Alipay roll back? If you don't roll back, what should you do? The same is true here. \ No newline at end of file + You can imagine your scenario is to call a third party payment. If you are doing a third-party payment operation, after calling Alipay's interface successfully, and your own code is wrong, will Alipay roll back? If you don't roll back, what should you do? The same is true here. diff --git a/docs/content/user-guide/zh/storage/general.md b/docs/content/user-guide/zh/storage/general.md index 05fea55..512e0b6 100644 --- a/docs/content/user-guide/zh/storage/general.md +++ b/docs/content/user-guide/zh/storage/general.md @@ -68,4 +68,10 @@ CallbackName | 回调的订阅者名称 | string 感谢社区对CAP的支持,以下是社区支持的持久化的实现 -* SQLite ([@colinin](https://github.com/colinin)) : https://github.com/colinin/DotNetCore.CAP.Sqlite +* SQLite ([@colinin](https://github.com/colinin)) :https://github.com/colinin/DotNetCore.CAP.Sqlite + +* LiteDB ([@maikebing](https://github.com/maikebing)) :https://github.com/maikebing/CAP.Extensions + +* SQLite & Oracle ([@cocosip](https://github.com/cocosip)) :https://github.com/cocosip/CAP-Extensions + +* SmartSql ([@xiangxiren](https://github.com/xiangxiren)) :https://github.com/xiangxiren/SmartSql.CAP diff --git a/docs/content/user-guide/zh/storage/sqlserver.md b/docs/content/user-guide/zh/storage/sqlserver.md index 1cd0ac6..be1f44f 100644 --- a/docs/content/user-guide/zh/storage/sqlserver.md +++ b/docs/content/user-guide/zh/storage/sqlserver.md @@ -25,7 +25,7 @@ public void ConfigureServices(IServiceCollection services) services.AddCap(x => { - x.UsePostgreSql(opt=>{ + x.UseSqlServer(opt=>{ //SqlServerOptions }); // x.UseXXX ... diff --git a/docs/content/user-guide/zh/transport/azure-service-bus.md b/docs/content/user-guide/zh/transport/azure-service-bus.md index ea12605..bd40682 100644 --- a/docs/content/user-guide/zh/transport/azure-service-bus.md +++ b/docs/content/user-guide/zh/transport/azure-service-bus.md @@ -45,4 +45,23 @@ NAME | DESCRIPTION | TYPE | DEFAULT :---|:---|---|:--- ConnectionString | Endpoint 地址 | string | TopicPath | Topic entity path | string | cap +EnableSessions | 启用 [Service bus sessions](https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-sessions) | bool | false ManagementTokenProvider | Token提供 | ITokenProvider | null + +#### Sessions + +当使用 `EnableSessions` 选项启用 sessions 后,每个发送的消息都会具有一个 session id。 要控制 seesion id 你可以在发送消息时在消息头中使用 `AzureServiceBusHeaders.SessionId` 携带它。 + + +```csharp +ICapPublisher capBus = ...; +string yourEventName = ...; +YourEventType yourEvent = ...; + +Dictionary extraHeaders = new Dictionary(); +extraHeaders.Add(AzureServiceBusHeaders.SessionId, ); + +capBus.Publish(yourEventName, yourEvent, extraHeaders); +``` + +如果头中没有 session id , 那么消息 Id 仍然使用的 Message Id. diff --git a/docs/content/user-guide/zh/transport/general.md b/docs/content/user-guide/zh/transport/general.md index 6fc75d1..13736ef 100644 --- a/docs/content/user-guide/zh/transport/general.md +++ b/docs/content/user-guide/zh/transport/general.md @@ -10,12 +10,14 @@ CAP 支持以下几种运输方式: * [Kafka](kafka.md) * [Azure Service Bus](azure-service-bus.md) * [Amazon SQS](aws-sqs.md) +* [NATS](nats.md) * [In-Memory Queue](in-memory-queue.md) +* [Redis Streams](redis-streams.md) ## 怎么选择运输器 🏳‍🌈 | RabbitMQ | Kafka | Azure Service Bus | In-Memory -:-- | :--: | :--: | :--: | :-- : +:-- | :--: | :--: | :--: | :--: **定位** | 可靠消息传输 | 实时数据处理 | 云 | 内存型,测试 **分布式** | ✔ | ✔ | ✔ |❌ **持久化** | ✔ | ✔ | ✔ | ❌ @@ -26,4 +28,14 @@ CAP 支持以下几种运输方式: > http://geekswithblogs.net/michaelstephenson/archive/2012/08/12/150399.aspx >`Kafka` vs `RabbitMQ` : -> https://stackoverflow.com/questions/42151544/is-there-any-reason-to-use-rabbitmq-over-kafka \ No newline at end of file +> https://stackoverflow.com/questions/42151544/is-there-any-reason-to-use-rabbitmq-over-kafka + +## 社区支持的运输器 + +感谢社区对CAP的支持,以下是社区支持的运输器实现 + +* ActiveMQ (@[Lukas Zhang](https://github.com/lukazh/Lukaz.CAP.ActiveMQ)): https://github.com/lukazh + +* RedisMQ ([@木木](https://github.com/difudotnet)): https://github.com/difudotnet/CAP.RedisMQ.Extensions + +* ZeroMQ ([@maikebing](https://github.com/maikebing)): https://github.com/maikebing/CAP.Extensions/tree/master/src/DotNetCore.CAP.ZeroMQ \ No newline at end of file diff --git a/docs/content/user-guide/zh/transport/nats.md b/docs/content/user-guide/zh/transport/nats.md new file mode 100644 index 0000000..68ffae1 --- /dev/null +++ b/docs/content/user-guide/zh/transport/nats.md @@ -0,0 +1,61 @@ +# NATS + +[NATS](https://nats.io/)是一个简单、安全、高性能的数字系统、服务和设备通信系统。NATS 是 CNCF 的一部分。 + +!!! warning + 我们当前基于 Request/Response 实现,我们计划将来版本中替换为 JetStream 。 + 查看 https://github.com/dotnetcore/CAP/issues/983 了解更多。 + +## 配置 + +要使用NATS 传输器,你需要安装下面的NuGet包: + +```powershell + +PM> Install-Package DotNetCore.CAP.NATS + +``` + +你可以通过在 `Startup.cs` 文件中配置 `ConfigureServices` 来添加配置: + +```csharp + +public void ConfigureServices(IServiceCollection services) +{ + services.AddCap(capOptions => + { + capOptions.UseNATS(natsOptions=>{ + //NATS Options + }); + }); +} + +``` + +#### NATS 配置 + +CAP 直接提供的关于 NATS 的配置参数: + + +NAME | DESCRIPTION | TYPE | DEFAULT +:---|:---|---|:--- +Options | NATS 客户端配置 | Options | Options +Servers | 服务器Urls地址 | string | NULL +ConnectionPoolSize | 连接池数量 | uint | 10 + +#### NATS ConfigurationOptions + +如果你需要 **更多** 原生相关的配置项,可以通过 `Options` 配置项进行设定: + +```csharp +services.AddCap(capOptions => +{ + capOptions.UseNATS(natsOptions=> + { + // NATS options. + natsOptions.Options.Url=""; + }); +}); +``` + +`Options` 是 NATS.Client 客户端提供的配置, 你可以在这个[链接](http://nats-io.github.io/nats.net/class_n_a_t_s_1_1_client_1_1_options.html)找到更多详细信息。 diff --git a/docs/content/user-guide/zh/transport/rabbitmq.md b/docs/content/user-guide/zh/transport/rabbitmq.md index ed9d2d9..29cbb89 100644 --- a/docs/content/user-guide/zh/transport/rabbitmq.md +++ b/docs/content/user-guide/zh/transport/rabbitmq.md @@ -45,8 +45,10 @@ UserName | 用户名 | string | guest Password | 密码 | string | guest VirtualHost | 虚拟主机 | string | / Port | 端口号 | int | -1 -TopicExchangeName | CAP默认Exchange名称 | string | cap.default.topic -QueueMessageExpires | 队列中消息自动删除时间 | int | (10天) 毫秒 +ExchangeName | CAP默认Exchange名称 | string | cap.default.topic +QueueArguments | 创建队列额外参数 x-arguments | QueueArgumentsOptions | N/A +ConnectionFactoryOptions | RabbitMQClient原生参数 | ConnectionFactory | N/A +CustomHeaders | 订阅者自定义头信息 | Func>> | N/A #### ConnectionFactory Options @@ -67,6 +69,26 @@ services.AddCap(x => ``` +#### CustomHeaders Options + +当需要从异构系统或者直接接收从RabbitMQ 控制台发送的消息时,由于 CAP 需要定义额外的头信息才能正常订阅,所以此时会出现异常。通过提供此参数来进行自定义头信息的设置来使订阅者正常工作。 + +你可以在这里找到有关 [头信息](../cap/messaging#异构系统集成) 的说明。 + +用法如下: + +```cs +x.UseRabbitMQ(aa => +{ + aa.CustomHeaders = e => new List> + { + new KeyValuePair(Headers.MessageId, SnowflakeId.Default().NextId().ToString()), + new KeyValuePair(Headers.MessageName, e.RoutingKey), + }; +}); +``` + + #### 如何连接 RabbitMQ 集群? 使用逗号分隔连接字符串即可,如下: diff --git a/docs/content/user-guide/zh/transport/redis-streams.md b/docs/content/user-guide/zh/transport/redis-streams.md new file mode 100644 index 0000000..4341d43 --- /dev/null +++ b/docs/content/user-guide/zh/transport/redis-streams.md @@ -0,0 +1,58 @@ +# Redis Streams + +[Redis](https://redis.io/) 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 + +[Redis Stream](https://redis.io/topics/streams-intro) 是 Redis 5.0 引入的一种新数据类型,它用一种仅附加的数据结构以更抽象的方式模拟日志数据结构。 + +Redis Streams 可以在 CAP 中用作消息传输器。 + +## 配置 + +要使用 Redis Streams 传输器,您需要从 NuGet 安装以下包: + +```powershell +PM> Install-Package DotNetCore.CAP.RedisStreams +``` + +然后,您可以在 `Startup.cs` 的 `ConfigureServices` 方法中添加基于 Redis Stream 的配置项。 + +```csharp + +public void ConfigureServices(IServiceCollection services) +{ + services.AddCap(capOptions => + { + capOptions.UseRedis(redisOptions=>{ + //redisOptions + }); + }); +} + +``` + +#### Redis Streams Options + +CAP 直接对外提供的 Redis Stream 配置参数如下: + +NAME | DESCRIPTION | TYPE | DEFAULT +:---|:---|---|:--- +Configuration | redis连接配置 (StackExchange.Redis) | ConfigurationOptions | ConfigurationOptions +StreamEntriesCount | 读取时从 stream 返回的条目数 | uint | 10 +ConnectionPoolSize | 连接池数 | uint | 10 + +#### Redis ConfigurationOptions + +如果需要**更多**原生Redis相关配置选项,您可以在 `Configuration` 选项中进行设置 : + +```csharp +services.AddCap(capOptions => +{ + capOptions.UseRedis(redisOptions=> + { + // redis options. + redisOptions.Configuration.EndPoints.Add(IPAddress.Loopback, 0); + }); +}); +``` + +`Configuration` 是 StackExchange.Redis ConfigurationOptions ,您可以通过此[链接](https://stackexchange.github.io/StackExchange.Redis/Configuration)找到更多详细信息。 diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 0d2daab..0b57828 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -10,7 +10,7 @@ edit_uri: 'edit/master/docs/content' docs_dir: 'content' # Copyright -copyright: Copyright © 2017 NCC, Maintained by the CAP Team. +copyright: Copyright © 2021 NCC, Maintained by the CAP Team. #theme: material @@ -19,18 +19,26 @@ theme: palette: primary: 'deep purple' accent: 'indigo' - language: 'en' + language: en include_sidebar: true logo: 'img/logo.svg' favicon: 'img/favicon.ico' features: - - tabs + - navigation.tabs + - navigation.instant i18n: prev: 'Previous' next: 'Next' #Customization extra: + alternate: + - name: English + link: /user-guide/en/getting-started/quick-start + lang: en + - name: 中文 + link: /user-guide/zh/getting-started/quick-start + lang: zh social: - icon: 'fontawesome/brands/github' link: 'https://github.com/dotnetcore/CAP' @@ -81,16 +89,18 @@ nav: - CAP: - Configuration: user-guide/en/cap/configuration.md - Messaging: user-guide/en/cap/messaging.md - - Sagas: user-guide/en/cap/sagas.md + - Filter: user-guide/en/cap/filter.md - Serialization: user-guide/en/cap/serialization.md - Transactions: user-guide/en/cap/transactions.md - Idempotence: user-guide/en/cap/idempotence.md - Transport: - - General: user-guide/en/transport/general.md - - RabbitMQ: user-guide/en/transport/rabbitmq.md + - General: user-guide/en/transport/general.md + - Amazon SQS: user-guide/en/transport/aws-sqs.md - Apache Kafka®: user-guide/en/transport/kafka.md - Azure Service Bus: user-guide/en/transport/azure-service-bus.md - - Amazon SQS: user-guide/en/transport/aws-sqs.md + - NATS: user-guide/en/transport/nats.md + - RabbitMQ: user-guide/en/transport/rabbitmq.md + - Redis Streams: user-guide/en/transport/redis-streams.md - In-Memory Queue: user-guide/en/transport/in-memory-queue.md - Storage: - General: user-guide/en/storage/general.md @@ -115,16 +125,18 @@ nav: - CAP: - 配置: user-guide/zh/cap/configuration.md - 消息: user-guide/zh/cap/messaging.md - - Sagas: user-guide/zh/cap/sagas.md + - 过滤器: user-guide/zh/cap/filter.md - 序列化: user-guide/zh/cap/serialization.md - - 运输: user-guide/zh/cap/transactions.md + - 事务: user-guide/zh/cap/transactions.md - 幂等性: user-guide/zh/cap/idempotence.md - 传输: - - 简介: user-guide/zh/transport/general.md - - RabbitMQ: user-guide/zh/transport/rabbitmq.md - - Apache Kafka®: user-guide/zh/transport/kafka.md + - 简介: user-guide/zh/transport/general.md + - Amazon SQS: user-guide/zh/transport/aws-sqs.md + - Apache Kafka®: user-guide/zh/transport/kafka.md - Azure Service Bus: user-guide/zh/transport/azure-service-bus.md - - Amazon SQS: user-guide/zh/transport/aws-sqs.md + - NATS: user-guide/zh/transport/nats.md + - RabbitMQ: user-guide/zh/transport/rabbitmq.md + - Redis Streams: user-guide/zh/transport/redis-streams.md - In-Memory Queue: user-guide/zh/transport/in-memory-queue.md - 存储: - 简介: user-guide/zh/storage/general.md @@ -139,6 +151,7 @@ nav: - 性能追踪: user-guide/zh/monitoring/diagnostics.md - 健康检查: user-guide/zh/monitoring/health-checks.md - 示例: + - Castle DynamicProxy: user-guide/zh/samples/castle.dynamicproxy.md - Github: user-guide/zh/samples/github.md - eShopOnContainers: user-guide/zh/samples/eshoponcontainers.md - FAQ: user-guide/zh/samples/faq.md @@ -146,3 +159,8 @@ nav: - Contact Us: about/contact-us.md - Release Notes: about/release-notes.md - License: about/license.md + +# Google Analytics +google_analytics: + - !!python/object/apply:os.getenv ["GOOGLE_ANALYTICS_KEY"] + - auto \ No newline at end of file diff --git a/samples/Sample.AmazonSQS.InMemory/Sample.AmazonSQS.InMemory.csproj b/samples/Sample.AmazonSQS.InMemory/Sample.AmazonSQS.InMemory.csproj index c8ed0b3..7acd5c7 100644 --- a/samples/Sample.AmazonSQS.InMemory/Sample.AmazonSQS.InMemory.csproj +++ b/samples/Sample.AmazonSQS.InMemory/Sample.AmazonSQS.InMemory.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 diff --git a/samples/Sample.AmazonSQS.InMemory/appsettings.json b/samples/Sample.AmazonSQS.InMemory/appsettings.json index 50fe9a3..ef6dc62 100644 --- a/samples/Sample.AmazonSQS.InMemory/appsettings.json +++ b/samples/Sample.AmazonSQS.InMemory/appsettings.json @@ -2,7 +2,7 @@ "Logging": { "IncludeScopes": false, "LogLevel": { - "Default": "Error" + "Default": "Information" } } } diff --git a/samples/Sample.ConsoleApp/Program.cs b/samples/Sample.ConsoleApp/Program.cs index f0315e1..8929988 100644 --- a/samples/Sample.ConsoleApp/Program.cs +++ b/samples/Sample.ConsoleApp/Program.cs @@ -16,7 +16,7 @@ namespace Sample.ConsoleApp { //console app does not support dashboard - x.UseMySql("Server=192.168.3.57;Port=3307;Database=captest;Uid=root;Pwd=123123;"); + x.UseMySql(""); x.UseRabbitMQ(z => { z.HostName = "192.168.3.57"; @@ -29,7 +29,7 @@ namespace Sample.ConsoleApp var sp = container.BuildServiceProvider(); - sp.GetService().BootstrapAsync(default); + sp.GetService().BootstrapAsync(); Console.ReadLine(); } diff --git a/samples/Sample.ConsoleApp/Sample.ConsoleApp.csproj b/samples/Sample.ConsoleApp/Sample.ConsoleApp.csproj index b2c9c03..cd8736f 100644 --- a/samples/Sample.ConsoleApp/Sample.ConsoleApp.csproj +++ b/samples/Sample.ConsoleApp/Sample.ConsoleApp.csproj @@ -1,12 +1,12 @@  - netcoreapp3.1 + net5.0 Exe - + diff --git a/samples/Sample.Dashboard.Auth/Controllers/ValuesController.cs b/samples/Sample.Dashboard.Auth/Controllers/ValuesController.cs new file mode 100644 index 0000000..88de8db --- /dev/null +++ b/samples/Sample.Dashboard.Auth/Controllers/ValuesController.cs @@ -0,0 +1,49 @@ +using System; +using System.Threading.Tasks; +using DotNetCore.CAP; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace Sample.Dashboard.Auth.Controllers +{ + [Authorize] + [Route("api/[controller]")] + public class ValuesController : Controller + { + private readonly ICapPublisher _capBus; + private readonly ILogger _logger; + private const string MyTopic = "sample.dashboard.auth"; + + public ValuesController(ICapPublisher capPublisher, ILogger logger) + { + _capBus = capPublisher; + _logger = logger; + } + + [Route("publish")] + public async Task Publish() + { + await _capBus.PublishAsync(MyTopic, new Person() + { + Id = new Random().Next(1, 100), + Name = "Bar" + }); + + return Ok(); + } + + [NonAction] + [CapSubscribe(MyTopic)] + public void Subscribe(Person p, [FromCap] CapHeader header) + { + _logger.LogInformation("Subscribe Invoked: " + MyTopic + p); + } + + public class Person + { + public int Id { get; set; } + public string Name { get; set; } + } + } +} \ No newline at end of file diff --git a/samples/Sample.Dashboard.Auth/MyDashboardAuthenticationHandler.cs b/samples/Sample.Dashboard.Auth/MyDashboardAuthenticationHandler.cs new file mode 100644 index 0000000..c4320b6 --- /dev/null +++ b/samples/Sample.Dashboard.Auth/MyDashboardAuthenticationHandler.cs @@ -0,0 +1,50 @@ +using System.Linq; +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Sample.Dashboard.Auth +{ + public class MyDashboardAuthenticationSchemeOptions : AuthenticationSchemeOptions + { + + } + + public class MyDashboardAuthenticationHandler : AuthenticationHandler + { + public MyDashboardAuthenticationHandler(IOptionsMonitor options, + ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) + { + options.CurrentValue.ForwardChallenge = ""; + } + + protected override Task HandleAuthenticateAsync() + { + var testAuthHeaderPresent = Request.Headers["X-Base-Token"].Contains("xxx"); + + var authResult = testAuthHeaderPresent ? AuthenticatedTestUser() : AuthenticateResult.NoResult(); + + return Task.FromResult(authResult); + } + + protected override Task HandleChallengeAsync(AuthenticationProperties properties) + { + Response.Headers["WWW-Authenticate"] = "MyDashboardScheme"; + + return base.HandleChallengeAsync(properties); + } + + private AuthenticateResult AuthenticatedTestUser() + { + var claims = new[] { new Claim(ClaimTypes.Name, "My Dashboard user") }; + var identity = new ClaimsIdentity(claims, "MyDashboardScheme"); + var principal = new ClaimsPrincipal(identity); + var ticket = new AuthenticationTicket(principal, "MyDashboardScheme"); + + return AuthenticateResult.Success(ticket); + } + } +} diff --git a/samples/Sample.Dashboard.Auth/Program.cs b/samples/Sample.Dashboard.Auth/Program.cs new file mode 100644 index 0000000..5438f82 --- /dev/null +++ b/samples/Sample.Dashboard.Auth/Program.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace Sample.Dashboard.Auth +{ + 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(); }); + } +} \ No newline at end of file diff --git a/samples/Sample.Dashboard.Auth/Sample.Dashboard.Auth.csproj b/samples/Sample.Dashboard.Auth/Sample.Dashboard.Auth.csproj new file mode 100644 index 0000000..363fb40 --- /dev/null +++ b/samples/Sample.Dashboard.Auth/Sample.Dashboard.Auth.csproj @@ -0,0 +1,18 @@ + + + + net5.0 + + + + + + + + + + + + + + diff --git a/samples/Sample.Dashboard.Auth/Startup.cs b/samples/Sample.Dashboard.Auth/Startup.cs new file mode 100644 index 0000000..0c2b00f --- /dev/null +++ b/samples/Sample.Dashboard.Auth/Startup.cs @@ -0,0 +1,94 @@ +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Sample.Dashboard.Auth +{ + public class Startup + { + private readonly IConfiguration _configuration; + + public Startup(IConfiguration configuration) + { + _configuration = configuration; + } + + public void ConfigureServices(IServiceCollection services) + { + services + .AddAuthorization() + .AddAuthentication(options => + { + options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; + }) + .AddCookie() + .AddOpenIdConnect(options => + { + options.Authority = "https://demo.identityserver.io/"; + options.ClientId = "interactive.confidential"; + options.ClientSecret = "secret"; + options.ResponseType = "code"; + options.UsePkce = true; + + options.Scope.Clear(); + options.Scope.Add("openid"); + options.Scope.Add("profile"); + }) + .AddScheme("MyDashboardScheme",null); + + services.AddCors(x => + { + x.AddDefaultPolicy(p => + { + p.WithOrigins("http://localhost:8080").AllowCredentials().AllowAnyHeader().AllowAnyMethod(); + }); + }); + + services.AddCap(cap => + { + cap.UseDashboard(d => + { + d.UseChallengeOnAuth = true; + d.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; + d.UseAuth = true; + d.DefaultAuthenticationScheme = "MyDashboardScheme"; + }); + cap.UseMySql(_configuration.GetValue("ConnectionString")); + cap.UseRabbitMQ(aa => + { + aa.HostName = "192.168.3.57"; + aa.UserName = "user"; + aa.Password = "wJ0p5gSs17"; + }); + //cap.UseDiscovery(_ => + //{ + // _.DiscoveryServerHostName = "localhost"; + // _.DiscoveryServerPort = 8500; + // _.CurrentNodeHostName = _configuration.GetValue("ASPNETCORE_HOSTNAME"); + // _.CurrentNodePort = _configuration.GetValue("ASPNETCORE_PORT"); + // _.NodeId = _configuration.GetValue("NodeId"); + // _.NodeName = _configuration.GetValue("NodeName"); + //}); + }); + + services.AddControllers(); + } + + public void Configure(IApplicationBuilder app) + { + app.UseCors(); + app.UseRouting(); + app.UseAuthentication(); + app.UseAuthorization(); + app.UseCookiePolicy(); + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } + +} \ No newline at end of file diff --git a/samples/Sample.Dashboard.Auth/appsettings.json b/samples/Sample.Dashboard.Auth/appsettings.json new file mode 100644 index 0000000..39f73ff --- /dev/null +++ b/samples/Sample.Dashboard.Auth/appsettings.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "Postgres": "Server=127.0.0.1;Port=5432;Database=cap;Uid=postgres;Pwd=root;Include Error Detail=true;" + } +} diff --git a/samples/Sample.Kafka.PostgreSql/Sample.Kafka.PostgreSql.csproj b/samples/Sample.Kafka.PostgreSql/Sample.Kafka.PostgreSql.csproj index 90d24a4..89f3ff3 100644 --- a/samples/Sample.Kafka.PostgreSql/Sample.Kafka.PostgreSql.csproj +++ b/samples/Sample.Kafka.PostgreSql/Sample.Kafka.PostgreSql.csproj @@ -1,13 +1,13 @@  - netcoreapp3.1 + net5.0 NU1701 NU1701 - + diff --git a/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj b/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj index 831dc9c..3b31902 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj +++ b/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 diff --git a/samples/Sample.RabbitMQ.MongoDB/Startup.cs b/samples/Sample.RabbitMQ.MongoDB/Startup.cs index bd80f11..b235a4c 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Startup.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Startup.cs @@ -20,7 +20,7 @@ namespace Sample.RabbitMQ.MongoDB services.AddCap(x => { x.UseMongoDB(Configuration.GetConnectionString("MongoDB")); - x.UseRabbitMQ("192.168.2.120"); + x.UseRabbitMQ(""); x.UseDashboard(); }); services.AddControllers(); diff --git a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs index 0481e10..9d01707 100644 --- a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs +++ b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs @@ -26,13 +26,13 @@ namespace Sample.RabbitMQ.MySql } public class AppDbContext : DbContext { - public const string ConnectionString = "Server=localhost;Database=testcap;UserId=root;Password=123123;"; + public const string ConnectionString = ""; public DbSet Persons { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - optionsBuilder.UseMySql(ConnectionString); + optionsBuilder.UseMySql(ConnectionString, ServerVersion.FromString("mysql")); } } } diff --git a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj index 099f9d6..7326bf6 100644 --- a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj +++ b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj @@ -1,12 +1,12 @@  - netcoreapp3.1 + net5.0 - - + + diff --git a/samples/Sample.RabbitMQ.SqlServer/AppDbContext.cs b/samples/Sample.RabbitMQ.SqlServer/AppDbContext.cs index fdad2a5..e8835c6 100644 --- a/samples/Sample.RabbitMQ.SqlServer/AppDbContext.cs +++ b/samples/Sample.RabbitMQ.SqlServer/AppDbContext.cs @@ -12,23 +12,11 @@ namespace Sample.RabbitMQ.SqlServer { return $"Name:{Name}, Id:{Id}"; } - } - - public class Person2 - { - public int Id { get; set; } - - public string Name { get; set; } - - public override string ToString() - { - return $"Name:{Name}, Id:{Id}"; - } - } + } public class AppDbContext : DbContext { - public const string ConnectionString = "Server=192.168.2.120;Database=captest;User Id=sa;Password=P@ssw0rd;"; + public const string ConnectionString = ""; public DbSet Persons { get; set; } diff --git a/samples/Sample.RabbitMQ.SqlServer/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.SqlServer/Controllers/ValuesController.cs index 24d1848..bb75269 100644 --- a/samples/Sample.RabbitMQ.SqlServer/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.SqlServer/Controllers/ValuesController.cs @@ -1,8 +1,8 @@ using System; -using System.Data; using System.Threading.Tasks; using Dapper; using DotNetCore.CAP; +using DotNetCore.CAP.Messages; using Microsoft.AspNetCore.Mvc; using Microsoft.Data.SqlClient; @@ -21,7 +21,7 @@ namespace Sample.RabbitMQ.SqlServer.Controllers [Route("~/without/transaction")] public async Task WithoutTransaction() { - await _capBus.PublishAsync("sample.rabbitmq.mysql", new Person() + await _capBus.PublishAsync("sample.rabbitmq.sqlserver", new Person() { Id = 123, Name = "Bar" @@ -40,7 +40,11 @@ namespace Sample.RabbitMQ.SqlServer.Controllers //your business code connection.Execute("insert into test(name) values('test')", transaction: transaction); - _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); + _capBus.Publish("sample.rabbitmq.sqlserver", new Person() + { + Id = 123, + Name = "Bar" + }); } } @@ -54,22 +58,28 @@ namespace Sample.RabbitMQ.SqlServer.Controllers { dbContext.Persons.Add(new Person() { Name = "ef.transaction" }); - _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); + _capBus.Publish("sample.rabbitmq.sqlserver", new Person() + { + Id = 123, + Name = "Bar" + }); } return Ok(); } [NonAction] - [CapSubscribe("sample.rabbitmq.mysql")] - public void Subscriber(DateTime p) + [CapSubscribe("sample.rabbitmq.sqlserver")] + public void Subscriber(Person p) { Console.WriteLine($@"{DateTime.Now} Subscriber invoked, Info: {p}"); } [NonAction] - [CapSubscribe("sample.rabbitmq.mysql", Group = "group.test2")] - public void Subscriber2(DateTime p, [FromCap]CapHeader header) + [CapSubscribe("sample.rabbitmq.sqlserver", Group = "group.test2")] + public void Subscriber2(Person p, [FromCap]CapHeader header) { + var id = header[Headers.MessageId]; + Console.WriteLine($@"{DateTime.Now} Subscriber invoked, Info: {p}"); } } diff --git a/samples/Sample.RabbitMQ.SqlServer/Sample.RabbitMQ.SqlServer.csproj b/samples/Sample.RabbitMQ.SqlServer/Sample.RabbitMQ.SqlServer.csproj index 69cae9b..64029d5 100644 --- a/samples/Sample.RabbitMQ.SqlServer/Sample.RabbitMQ.SqlServer.csproj +++ b/samples/Sample.RabbitMQ.SqlServer/Sample.RabbitMQ.SqlServer.csproj @@ -1,16 +1,16 @@  - netcoreapp3.1 + net5.0 - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/samples/Sample.RabbitMQ.SqlServer/Startup.cs b/samples/Sample.RabbitMQ.SqlServer/Startup.cs index a309637..ec0883b 100644 --- a/samples/Sample.RabbitMQ.SqlServer/Startup.cs +++ b/samples/Sample.RabbitMQ.SqlServer/Startup.cs @@ -1,4 +1,6 @@ using System; +using System.Text.Encodings.Web; +using System.Text.Unicode; using DotNetCore.CAP.Messages; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; @@ -15,7 +17,7 @@ namespace Sample.RabbitMQ.SqlServer services.AddCap(x => { x.UseEntityFramework(); - x.UseRabbitMQ("192.168.2.120"); + x.UseRabbitMQ(""); x.UseDashboard(); x.FailedRetryCount = 5; x.FailedThresholdCallback = failed => @@ -24,6 +26,7 @@ namespace Sample.RabbitMQ.SqlServer logger.LogError($@"A message of type {failed.MessageType} failed after executing {x.FailedRetryCount} several times, requiring manual troubleshooting. Message name: {failed.Message.GetName()}"); }; + x.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); }); services.AddControllers(); diff --git a/samples/Samples.Redis.SqlServer/Controllers/HomeController.cs b/samples/Samples.Redis.SqlServer/Controllers/HomeController.cs new file mode 100644 index 0000000..46773cc --- /dev/null +++ b/samples/Samples.Redis.SqlServer/Controllers/HomeController.cs @@ -0,0 +1,54 @@ +using DotNetCore.CAP; +using DotNetCore.CAP.Messages; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System.Threading.Tasks; + +namespace Samples.Redis.SqlServer.Controllers +{ + [ApiController] + [Route("[controller]/[action]")] + public class HomeController : ControllerBase + { + private readonly ILogger _logger; + private readonly ICapPublisher _publisher; + private readonly IOptions _options; + + public HomeController(ILogger logger, ICapPublisher publisher, IOptions options) + { + _logger = logger; + _publisher = publisher; + this._options = options; + } + + [HttpGet] + public async Task Publish([FromQuery] string message = "test-message") + { + await _publisher.PublishAsync(message, new Person() { Age = 11, Name = "James" }); + } + + [CapSubscribe("test-message")] + [CapSubscribe("test-message-1")] + [CapSubscribe("test-message-2")] + [CapSubscribe("test-message-3")] + [NonAction] + public void Subscribe(Person p, [FromCap] CapHeader header) + { + _logger.LogInformation($"{header[Headers.MessageName]} subscribed with value --> " + p); + } + + } + + public class Person + { + public string Name { get; set; } + + public int Age { get; set; } + + public override string ToString() + { + return "Name:" + Name + ", Age:" + Age; + } + } +} diff --git a/samples/Samples.Redis.SqlServer/Dockerfile b/samples/Samples.Redis.SqlServer/Dockerfile new file mode 100644 index 0000000..a5c861c --- /dev/null +++ b/samples/Samples.Redis.SqlServer/Dockerfile @@ -0,0 +1,25 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build +WORKDIR /src +COPY ["samples/Samples.Redis.SqlServer/Samples.Redis.SqlServer.csproj", "samples/Samples.Redis.SqlServer/"] +COPY ["src/DotNetCore.CAP.RedisStreams/DotNetCore.CAP.RedisStreams.csproj", "src/DotNetCore.CAP.RedisStreams/"] +COPY ["src/DotNetCore.CAP/DotNetCore.CAP.csproj", "src/DotNetCore.CAP/"] +COPY ["src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj", "src/DotNetCore.CAP.SqlServer/"] +RUN dotnet restore "samples/Samples.Redis.SqlServer/Samples.Redis.SqlServer.csproj" +COPY . . +WORKDIR "/src/samples/Samples.Redis.SqlServer" +RUN dotnet build "Samples.Redis.SqlServer.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Samples.Redis.SqlServer.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Samples.Redis.SqlServer.dll"] \ No newline at end of file diff --git a/samples/Samples.Redis.SqlServer/Program.cs b/samples/Samples.Redis.SqlServer/Program.cs new file mode 100644 index 0000000..4cb1244 --- /dev/null +++ b/samples/Samples.Redis.SqlServer/Program.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace Samples.Redis.SqlServer +{ + 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(); + }); + } +} diff --git a/samples/Samples.Redis.SqlServer/Samples.Redis.SqlServer.csproj b/samples/Samples.Redis.SqlServer/Samples.Redis.SqlServer.csproj new file mode 100644 index 0000000..e8ffe9f --- /dev/null +++ b/samples/Samples.Redis.SqlServer/Samples.Redis.SqlServer.csproj @@ -0,0 +1,14 @@ + + + + net5.0 + + + + + + + + + + diff --git a/samples/Samples.Redis.SqlServer/Startup.cs b/samples/Samples.Redis.SqlServer/Startup.cs new file mode 100644 index 0000000..adad4ab --- /dev/null +++ b/samples/Samples.Redis.SqlServer/Startup.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OpenApi.Models; + +namespace Samples.Redis.SqlServer +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "Samples.Redis.SqlServer", Version = "v1" }); + }); + + + services.AddCap(options => + { + options.UseRedis("redis-node-0:6379,password=cap"); + + options.UseSqlServer("Server=db;Database=master;User=sa;Password=P@ssw0rd;"); + }); + } + + public void Configure(IApplicationBuilder app) + { + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Samples.Redis.SqlServer v1")); + + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/samples/Samples.Redis.SqlServer/appsettings.Development.json b/samples/Samples.Redis.SqlServer/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/samples/Samples.Redis.SqlServer/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/samples/Samples.Redis.SqlServer/appsettings.json b/samples/Samples.Redis.SqlServer/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/samples/Samples.Redis.SqlServer/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/samples/Samples.Redis.SqlServer/docker-compose.yml b/samples/Samples.Redis.SqlServer/docker-compose.yml new file mode 100644 index 0000000..a74cd8b --- /dev/null +++ b/samples/Samples.Redis.SqlServer/docker-compose.yml @@ -0,0 +1,90 @@ +version: '2' +services: + redis-node-0: + image: docker.io/bitnami/redis-cluster:6.2 + volumes: + - redis-cluster_data-0:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=cap' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + + redis-node-1: + image: docker.io/bitnami/redis-cluster:6.2 + volumes: + - redis-cluster_data-1:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=cap' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + + redis-node-2: + image: docker.io/bitnami/redis-cluster:6.2 + volumes: + - redis-cluster_data-2:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=cap' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + + redis-node-3: + image: docker.io/bitnami/redis-cluster:6.2 + volumes: + - redis-cluster_data-3:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=cap' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + + redis-node-4: + image: docker.io/bitnami/redis-cluster:6.2 + volumes: + - redis-cluster_data-4:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=cap' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + + redis-node-5: + image: docker.io/bitnami/redis-cluster:6.2 + volumes: + - redis-cluster_data-5:/bitnami/redis/data + depends_on: + - redis-node-0 + - redis-node-1 + - redis-node-2 + - redis-node-3 + - redis-node-4 + environment: + - 'REDIS_PASSWORD=cap' + - 'REDISCLI_AUTH=cap' + - 'REDIS_CLUSTER_REPLICAS=1' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + - 'REDIS_CLUSTER_CREATOR=yes' + + db: + image: "mcr.microsoft.com/mssql/server" + ports: + - 1433:1433 + environment: + SA_PASSWORD: "P@ssw0rd" + ACCEPT_EULA: "Y" + + redis-sample: + build: + context: ../.. + dockerfile: samples/Samples.Redis.SqlServer/Dockerfile + ports: + - 5000:80 + depends_on: + - db + - redis-node-5 + +volumes: + redis-cluster_data-0: + driver: local + redis-cluster_data-1: + driver: local + redis-cluster_data-2: + driver: local + redis-cluster_data-3: + driver: local + redis-cluster_data-4: + driver: local + redis-cluster_data-5: + driver: local diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 9b58ae9..a30ba1b 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -26,7 +26,7 @@ - + \ No newline at end of file diff --git a/src/DotNetCore.CAP.AmazonSQS/AmazonPolicyExtensions.cs b/src/DotNetCore.CAP.AmazonSQS/AmazonPolicyExtensions.cs new file mode 100644 index 0000000..d3af025 --- /dev/null +++ b/src/DotNetCore.CAP.AmazonSQS/AmazonPolicyExtensions.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Amazon.Auth.AccessControlPolicy; +using Amazon.Auth.AccessControlPolicy.ActionIdentifiers; + +namespace DotNetCore.CAP.AmazonSQS +{ + public static class AmazonPolicyExtensions + { + /// + /// Check to see if the policy for the queue has already given permission to the topic. + /// + /// + /// + /// + /// + public static bool HasSqsPermission(this Policy policy, string topicArn, string sqsQueueArn) + { + foreach (var statement in policy.Statements) + { + var containsResource = statement.Resources.Any(r => r.Id.Equals(sqsQueueArn)); + + if (!containsResource) + { + continue; + } + + foreach (var condition in statement.Conditions) + { + if ((string.Equals(condition.Type, ConditionFactory.StringComparisonType.StringLike.ToString(), StringComparison.OrdinalIgnoreCase) || + string.Equals(condition.Type, ConditionFactory.StringComparisonType.StringEquals.ToString(), StringComparison.OrdinalIgnoreCase) || + string.Equals(condition.Type, ConditionFactory.ArnComparisonType.ArnEquals.ToString(), StringComparison.OrdinalIgnoreCase) || + string.Equals(condition.Type, ConditionFactory.ArnComparisonType.ArnLike.ToString(), StringComparison.OrdinalIgnoreCase)) && + string.Equals(condition.ConditionKey, ConditionFactory.SOURCE_ARN_CONDITION_KEY, StringComparison.OrdinalIgnoreCase) && + condition.Values.Contains(topicArn)) + { + return true; + } + } + } + + return false; + } + + /// + /// Add statement to the SQS policy that gives the SNS topics access to send a message to the queue. + /// + /// + /// { + /// "Version": "2012-10-17", + /// "Statement": [ + /// { + /// "Effect": "Allow", + /// "Principal": { + /// "AWS": "*" + /// }, + /// "Action": "sqs:SendMessage", + /// "Resource": "arn:aws:sqs:us-east-1:MyQueue", + /// "Condition": { + /// "ArnLike": { + /// "aws:SourceArn": [ + /// "arn:aws:sns:us-east-1:FirstTopic", + /// "arn:aws:sns:us-east-1:SecondTopic" + /// ] + /// } + /// } + /// }] + /// } + /// + /// + /// + /// + public static void AddSqsPermissions(this Policy policy, IEnumerable topicArns, string sqsQueueArn) + { + var statement = new Statement(Statement.StatementEffect.Allow); +#pragma warning disable CS0618 // Type or member is obsolete + statement.Actions.Add(SQSActionIdentifiers.SendMessage); +#pragma warning restore CS0618 // Type or member is obsolete + statement.Resources.Add(new Resource(sqsQueueArn)); + statement.Principals.Add(new Principal("*")); + foreach (var topicArn in topicArns) + { + statement.Conditions.Add(ConditionFactory.NewSourceArnCondition(topicArn)); + } + + policy.Statements.Add(statement); + } + + /// + /// Compact SQS access policy + /// + /// + /// Transforms policies with multiple similar statements: + /// + /// { + /// "Version": "2012-10-17", + /// "Statement": [ + /// { + /// "Effect": "Allow", + /// "Principal": { + /// "AWS": "*" + /// }, + /// "Action": "sqs:SendMessage", + /// "Resource": "arn:aws:sqs:us-east-1:MyQueue-v1", + /// "Condition": { + /// "ArnLike": { + /// "aws:SourceArn": "arn:aws:sns:us-east-1:MyQueue-FirstTopic" + /// } + /// } + /// }, + /// { + /// "Effect": "Allow", + /// "Principal": { + /// "AWS": "*" + /// }, + /// "Action": "sqs:SendMessage", + /// "Resource": "arn:aws:sqs:us-east-1:MyQueue-v1", + /// "Condition": { + /// "ArnLike": { + /// "aws:SourceArn": "arn:aws:sns:us-east-1:MyQueue-SecondTopic" + /// } + /// } + /// }, + /// { + /// "Effect": "Allow", + /// "Principal": { + /// "AWS": "*" + /// }, + /// "Action": "sqs:SendMessage", + /// "Resource": "arn:aws:sqs:us-east-1:MyQueue-v1", + /// "Condition": { + /// "ArnLike": { + /// "aws:SourceArn": "arn:aws:sns:us-east-1:MyQueue2-FirstTopic" + /// } + /// } + /// },] + /// } + /// + /// into compacted single statement: + /// + /// { + /// "Version": "2012-10-17", + /// "Statement": [ + /// { + /// "Effect": "Allow", + /// "Principal": { + /// "AWS": "*" + /// }, + /// "Action": "sqs:SendMessage", + /// "Resource": "arn:aws:sqs:us-east-1:MyQueue-v1", + /// "Condition": { + /// "ArnLike": { + /// "aws:SourceArn": [ + /// "arn:aws:sns:us-east-1:MyQueue-*", + /// "arn:aws:sns:us-east-1:MyQueue2-FirstTopic" + /// ] + /// } + /// } + /// }] + /// } + /// + /// + /// + /// + public static void CompactSqsPermissions(this Policy policy, string sqsQueueArn) + { + var statementsToCompact = policy.Statements + .Where(s => s.Effect == Statement.StatementEffect.Allow) +#pragma warning disable CS0618 // Type or member is obsolete + .Where(s => s.Actions.All(a => string.Equals(a.ActionName, SQSActionIdentifiers.SendMessage.ActionName, StringComparison.OrdinalIgnoreCase))) +#pragma warning restore CS0618 // Type or member is obsolete + .Where(s => s.Resources.All(r => string.Equals(r.Id, sqsQueueArn, StringComparison.OrdinalIgnoreCase))) + .Where(s => s.Principals.All(r => string.Equals(r.Id, "*", StringComparison.OrdinalIgnoreCase))) + .ToList(); + + var groupName = GetGroupName(sqsQueueArn); + if (groupName != null) + { + groupName = $":{groupName}-"; + } + if (statementsToCompact.Count < 2 && groupName == null) + { + return; + } + + var topicArns = new HashSet(); + foreach (var statement in statementsToCompact) + { + policy.Statements.Remove(statement); + foreach (var topicArn in statement.Conditions.SelectMany(c => c.Values)) + { + topicArns.Add( + groupName != null && topicArn.Contains(groupName, StringComparison.InvariantCultureIgnoreCase) + ? $"{GetArnGroupPrefix(topicArn)}-*" + : topicArn); + } + } + + policy.AddSqsPermissions(topicArns.OrderBy(a => a), sqsQueueArn); + } + + /// + /// Extract group prefix from ARN + /// For example for ARN: + /// arn:aws:sns:us-east-1:MyQueue-FirstTopic + /// group prefix will be extracted: + /// arn:aws:sns:us-east-1:MyQueue + /// + /// Source ARN + /// Group prefix or null if group not present + private static string GetArnGroupPrefix(string arn) + { + const char separator = '-'; + if (string.IsNullOrEmpty(arn) || !arn.Contains(separator)) + { + return null; + } + + var groupPaths = arn.Split(separator); + if (groupPaths.Length < 2) + { + return null; + } + + return string.Join(separator, groupPaths.Take(groupPaths.Length - 1)); + } + + /// + /// Extract group name from ARN + /// For example for ARN: + /// arn:aws:sns:us-east-1:MyQueue-FirstTopic + /// group name will be extracted: + /// MyQueue + /// + /// Source ARN + /// Group name or null if group not present + private static string GetGroupName(string arn) + { + const char separator = ':'; + if (string.IsNullOrEmpty(arn) || !arn.Contains(separator)) + { + return null; + } + + var name = arn.Split(separator).LastOrDefault(); + if(string.IsNullOrEmpty(name)) + { + return null; + } + + return GetArnGroupPrefix(name); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.AmazonSQS/AmazonSQSConsumerClient.cs b/src/DotNetCore.CAP.AmazonSQS/AmazonSQSConsumerClient.cs index 75b933a..a6b6c5c 100644 --- a/src/DotNetCore.CAP.AmazonSQS/AmazonSQSConsumerClient.cs +++ b/src/DotNetCore.CAP.AmazonSQS/AmazonSQSConsumerClient.cs @@ -5,8 +5,10 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Amazon.Auth.AccessControlPolicy; using Amazon.SimpleNotificationService; using Amazon.SimpleNotificationService.Model; using Amazon.SQS; @@ -14,7 +16,6 @@ using Amazon.SQS.Model; using DotNetCore.CAP.Messages; using DotNetCore.CAP.Transport; using Microsoft.Extensions.Options; -using Newtonsoft.Json; using Headers = DotNetCore.CAP.Messages.Headers; namespace DotNetCore.CAP.AmazonSQS @@ -42,17 +43,17 @@ namespace DotNetCore.CAP.AmazonSQS public BrokerAddress BrokerAddress => new BrokerAddress("AmazonSQS", _queueUrl); - public void Subscribe(IEnumerable topics) + public ICollection FetchTopics(IEnumerable topicNames) { - if (topics == null) + if (topicNames == null) { - throw new ArgumentNullException(nameof(topics)); + throw new ArgumentNullException(nameof(topicNames)); } - + Connect(initSNS: true, initSQS: false); - + var topicArns = new List(); - foreach (var topic in topics) + foreach (var topic in topicNames) { var createTopicRequest = new CreateTopicRequest(topic.NormalizeForAws()); @@ -60,11 +61,23 @@ namespace DotNetCore.CAP.AmazonSQS topicArns.Add(createTopicResponse.TopicArn); } + + GenerateSqsAccessPolicyAsync(topicArns) + .GetAwaiter().GetResult(); - Connect(initSNS: false, initSQS: true); + return topicArns; + } - _snsClient.SubscribeQueueToTopicsAsync(topicArns, _sqsClient, _queueUrl) - .GetAwaiter().GetResult(); + public void Subscribe(IEnumerable topics) + { + if (topics == null) + { + throw new ArgumentNullException(nameof(topics)); + } + + Connect(); + + SubscribeToTopics(topics).GetAwaiter().GetResult(); } public void Listening(TimeSpan timeout, CancellationToken cancellationToken) @@ -83,7 +96,7 @@ namespace DotNetCore.CAP.AmazonSQS if (response.Messages.Count == 1) { - var messageObj = JsonConvert.DeserializeObject(response.Messages[0].Body); + var messageObj = JsonSerializer.Deserialize(response.Messages[0].Body); var header = messageObj.MessageAttributes.ToDictionary(x => x.Key, x => x.Value.Value); var body = messageObj.Message; @@ -207,6 +220,51 @@ namespace DotNetCore.CAP.AmazonSQS return Task.CompletedTask; } + private async Task GenerateSqsAccessPolicyAsync(IEnumerable topicArns) + { + Connect(initSNS: false, initSQS: true); + + var queueAttributes = await _sqsClient.GetAttributesAsync(_queueUrl).ConfigureAwait(false); + + var sqsQueueArn = queueAttributes["QueueArn"]; + + var policy = queueAttributes.TryGetValue("Policy", out var policyStr) && !string.IsNullOrEmpty(policyStr) + ? Policy.FromJson(policyStr) + : new Policy(); + + var topicArnsToAllow = topicArns + .Where(a => !policy.HasSqsPermission(a, sqsQueueArn)) + .ToList(); + + if (!topicArnsToAllow.Any()) + { + return; + } + + policy.AddSqsPermissions(topicArnsToAllow, sqsQueueArn); + policy.CompactSqsPermissions(sqsQueueArn); + + var setAttributes = new Dictionary { { "Policy", policy.ToJson() } }; + await _sqsClient.SetAttributesAsync(_queueUrl, setAttributes).ConfigureAwait(false); + } + + private async Task SubscribeToTopics(IEnumerable topics) + { + var queueAttributes = await _sqsClient.GetAttributesAsync(_queueUrl).ConfigureAwait(false); + + var sqsQueueArn = queueAttributes["QueueArn"]; + foreach (var topicArn in topics) + { + await _snsClient.SubscribeAsync(new SubscribeRequest + { + TopicArn = topicArn, + Protocol = "sqs", + Endpoint = sqsQueueArn, + }) + .ConfigureAwait(false); + } + } + #endregion } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.AmazonSQS/DotNetCore.CAP.AmazonSQS.csproj b/src/DotNetCore.CAP.AmazonSQS/DotNetCore.CAP.AmazonSQS.csproj index 5a7116b..bf4470f 100644 --- a/src/DotNetCore.CAP.AmazonSQS/DotNetCore.CAP.AmazonSQS.csproj +++ b/src/DotNetCore.CAP.AmazonSQS/DotNetCore.CAP.AmazonSQS.csproj @@ -1,23 +1,23 @@  - netstandard2.0 + netstandard2.1 DotNetCore.CAP.AmazonSQS $(PackageTags);AmazonSQS;SQS - bin\$(Configuration)\netstandard2.0\DotNetCore.CAP.AmazonSQS.xml + bin\$(Configuration)\netstandard2.1\DotNetCore.CAP.AmazonSQS.xml 1701;1702;1705;CS1591 - - + + - + \ No newline at end of file diff --git a/src/DotNetCore.CAP.AmazonSQS/ITransport.AmazonSQS.cs b/src/DotNetCore.CAP.AmazonSQS/ITransport.AmazonSQS.cs index 2ab3bf6..c8499e9 100644 --- a/src/DotNetCore.CAP.AmazonSQS/ITransport.AmazonSQS.cs +++ b/src/DotNetCore.CAP.AmazonSQS/ITransport.AmazonSQS.cs @@ -37,9 +37,9 @@ namespace DotNetCore.CAP.AmazonSQS { try { - await TryAddTopicArns(); + await FetchExistingTopicArns(); - if (_topicArnMaps.TryGetValue(message.GetName().NormalizeForAws(), out var arn)) + if (TryGetOrCreateTopicArn(message.GetName().NormalizeForAws(), out var arn)) { string bodyJson = null; if (message.Body != null) @@ -62,12 +62,19 @@ namespace DotNetCore.CAP.AmazonSQS await _snsClient.PublishAsync(request); _logger.LogDebug($"SNS topic message [{message.GetName().NormalizeForAws()}] has been published."); + return OperateResult.Success; } - else - { - _logger.LogWarning($"Can't be found SNS topics for [{message.GetName().NormalizeForAws()}]"); - } - return OperateResult.Success; + + var errorMessage = $"Can't be found SNS topics for [{message.GetName().NormalizeForAws()}]"; + _logger.LogWarning(errorMessage); + + return OperateResult.Failed( + new PublisherSentFailedException(errorMessage), + new OperateError + { + Code = "SNS", + Description = $"Can't be found SNS topics for [{message.GetName().NormalizeForAws()}]" + }); } catch (Exception ex) { @@ -82,11 +89,11 @@ namespace DotNetCore.CAP.AmazonSQS } } - public async Task TryAddTopicArns() + private async Task FetchExistingTopicArns() { if (_topicArnMaps != null) { - return true; + return; } await _semaphore.WaitAsync(); @@ -100,14 +107,21 @@ namespace DotNetCore.CAP.AmazonSQS if (_topicArnMaps == null) { _topicArnMaps = new Dictionary(); - var topics = await _snsClient.ListTopicsAsync(); - topics.Topics.ForEach(x => + + string nextToken = null; + do { - var name = x.TopicArn.Split(':').Last(); - _topicArnMaps.Add(name, x.TopicArn); - }); - - return true; + var topics = nextToken == null + ? await _snsClient.ListTopicsAsync() + : await _snsClient.ListTopicsAsync(nextToken); + topics.Topics.ForEach(x => + { + var name = x.TopicArn.Split(':').Last(); + _topicArnMaps.Add(name, x.TopicArn); + }); + nextToken = topics.NextToken; + } + while (!string.IsNullOrEmpty(nextToken)); } } catch (Exception e) @@ -118,8 +132,27 @@ namespace DotNetCore.CAP.AmazonSQS { _semaphore.Release(); } + } + + private bool TryGetOrCreateTopicArn(string topicName, out string topicArn) + { + topicArn = null; + if (_topicArnMaps.TryGetValue(topicName, out topicArn)) + { + return true; + } + + var response = _snsClient.CreateTopicAsync(topicName).GetAwaiter().GetResult(); - return false; + if (string.IsNullOrEmpty(response.TopicArn)) + { + return false; + } + + topicArn = response.TopicArn; + + _topicArnMaps.Add(topicName, topicArn); + return true; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs b/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs index de94ad9..ee5d84b 100644 --- a/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs +++ b/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs @@ -78,13 +78,25 @@ namespace DotNetCore.CAP.AzureServiceBus { ConnectAsync().GetAwaiter().GetResult(); - _consumerClient.RegisterMessageHandler(OnConsumerReceived, - new MessageHandlerOptions(OnExceptionReceived) - { - AutoComplete = false, - MaxConcurrentCalls = 10, - MaxAutoRenewDuration = TimeSpan.FromSeconds(30) - }); + if (_asbOptions.EnableSessions) + { + _consumerClient.RegisterSessionHandler(OnConsumerReceivedWithSession, + new SessionHandlerOptions(OnExceptionReceived) + { + AutoComplete = false, + MaxAutoRenewDuration = TimeSpan.FromSeconds(30) + }); + } + else + { + _consumerClient.RegisterMessageHandler(OnConsumerReceived, + new MessageHandlerOptions(OnExceptionReceived) + { + AutoComplete = false, + MaxConcurrentCalls = 10, + MaxAutoRenewDuration = TimeSpan.FromSeconds(30) + }); + } while (true) { @@ -96,7 +108,15 @@ namespace DotNetCore.CAP.AzureServiceBus public void Commit(object sender) { - _consumerClient.CompleteAsync((string)sender); + var commitInput = (AzureServiceBusConsumerCommitInput) sender; + if (_asbOptions.EnableSessions) + { + commitInput.Session.CompleteAsync(commitInput.LockToken); + } + else + { + _consumerClient.CompleteAsync(commitInput.LockToken); + } } public void Reject(object sender) @@ -141,7 +161,13 @@ namespace DotNetCore.CAP.AzureServiceBus if (!await mClient.SubscriptionExistsAsync(_asbOptions.TopicPath, _subscriptionName)) { - await mClient.CreateSubscriptionAsync(_asbOptions.TopicPath, _subscriptionName); + var subscriptionDescription = + new SubscriptionDescription(_asbOptions.TopicPath, _subscriptionName) + { + RequiresSession = _asbOptions.EnableSessions + }; + + await mClient.CreateSubscriptionAsync(subscriptionDescription); _logger.LogInformation($"Azure Service Bus topic {_asbOptions.TopicPath} created subscription: {_subscriptionName}"); } @@ -157,14 +183,28 @@ namespace DotNetCore.CAP.AzureServiceBus #region private methods - private Task OnConsumerReceived(Message message, CancellationToken token) + private TransportMessage ConvertMessage(Message message) { var header = message.UserProperties.ToDictionary(x => x.Key, y => y.Value?.ToString()); header.Add(Headers.Group, _subscriptionName); - var context = new TransportMessage(header, message.Body); + return new TransportMessage(header, message.Body); + } + + private Task OnConsumerReceivedWithSession(IMessageSession session, Message message, CancellationToken token) + { + var context = ConvertMessage(message); + + OnMessageReceived?.Invoke(new AzureServiceBusConsumerCommitInput(message.SystemProperties.LockToken, session), context); + + return Task.CompletedTask; + } + + private Task OnConsumerReceived(Message message, CancellationToken token) + { + var context = ConvertMessage(message); - OnMessageReceived?.Invoke(message.SystemProperties.LockToken, context); + OnMessageReceived?.Invoke(new AzureServiceBusConsumerCommitInput(message.SystemProperties.LockToken), context); return Task.CompletedTask; } diff --git a/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerCommitInput.cs b/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerCommitInput.cs new file mode 100644 index 0000000..c76a360 --- /dev/null +++ b/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerCommitInput.cs @@ -0,0 +1,16 @@ +using Microsoft.Azure.ServiceBus; + +namespace DotNetCore.CAP.AzureServiceBus +{ + public class AzureServiceBusConsumerCommitInput + { + public AzureServiceBusConsumerCommitInput(string lockToken, IMessageSession session = null) + { + LockToken = lockToken; + Session = session; + } + + public IMessageSession Session { get; set; } + public string LockToken { get; set; } + } +} diff --git a/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusHeaders.cs b/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusHeaders.cs new file mode 100644 index 0000000..89723a0 --- /dev/null +++ b/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusHeaders.cs @@ -0,0 +1,7 @@ +namespace DotNetCore.CAP.AzureServiceBus +{ + public static class AzureServiceBusHeaders + { + public const string SessionId = "cap-session-id"; + } +} diff --git a/src/DotNetCore.CAP.AzureServiceBus/CAP.AzureServiceBusOptions.cs b/src/DotNetCore.CAP.AzureServiceBus/CAP.AzureServiceBusOptions.cs index ef78199..61c6d63 100644 --- a/src/DotNetCore.CAP.AzureServiceBus/CAP.AzureServiceBusOptions.cs +++ b/src/DotNetCore.CAP.AzureServiceBus/CAP.AzureServiceBusOptions.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Core Community. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using DotNetCore.CAP.AzureServiceBus; using Microsoft.Azure.ServiceBus.Primitives; // ReSharper disable once CheckNamespace @@ -21,6 +22,12 @@ namespace DotNetCore.CAP /// public string ConnectionString { get; set; } + /// + /// Whether Service Bus sessions are enabled. If enabled, all messages must contain a + /// header. Defaults to false. + /// + public bool EnableSessions { get; set; } = false; + /// /// The name of the topic relative to the service namespace base address. /// diff --git a/src/DotNetCore.CAP.AzureServiceBus/DotNetCore.CAP.AzureServiceBus.csproj b/src/DotNetCore.CAP.AzureServiceBus/DotNetCore.CAP.AzureServiceBus.csproj index f73f0fb..c7d82a7 100644 --- a/src/DotNetCore.CAP.AzureServiceBus/DotNetCore.CAP.AzureServiceBus.csproj +++ b/src/DotNetCore.CAP.AzureServiceBus/DotNetCore.CAP.AzureServiceBus.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.1 DotNetCore.CAP.AzureServiceBus $(PackageTags);AzureServiceBus @@ -9,15 +9,15 @@ NU1605;NU1701 NU1701;CS1591 - bin\$(Configuration)\netstandard2.0\DotNetCore.CAP.AzureServiceBus.xml + bin\$(Configuration)\netstandard2.1\DotNetCore.CAP.AzureServiceBus.xml - + - + \ No newline at end of file diff --git a/src/DotNetCore.CAP.AzureServiceBus/ITransport.AzureServiceBus.cs b/src/DotNetCore.CAP.AzureServiceBus/ITransport.AzureServiceBus.cs index 763e381..3038c5d 100644 --- a/src/DotNetCore.CAP.AzureServiceBus/ITransport.AzureServiceBus.cs +++ b/src/DotNetCore.CAP.AzureServiceBus/ITransport.AzureServiceBus.cs @@ -42,9 +42,16 @@ namespace DotNetCore.CAP.AzureServiceBus { MessageId = transportMessage.GetId(), Body = transportMessage.Body, - Label = transportMessage.GetName() + Label = transportMessage.GetName(), + CorrelationId = transportMessage.GetCorrelationId() }; + if (_asbOptions.Value.EnableSessions) + { + transportMessage.Headers.TryGetValue(AzureServiceBusHeaders.SessionId, out var sessionId); + message.SessionId = string.IsNullOrEmpty(sessionId) ? transportMessage.GetId() : sessionId; + } + foreach (var header in transportMessage.Headers) { message.UserProperties.Add(header.Key, header.Value); @@ -75,10 +82,7 @@ namespace DotNetCore.CAP.AzureServiceBus try { - if (_topicClient == null) - { - _topicClient = new TopicClient(BrokerAddress.Endpoint, _asbOptions.Value.TopicPath, RetryPolicy.NoRetry); - } + _topicClient ??= new TopicClient(BrokerAddress.Endpoint, _asbOptions.Value.TopicPath, RetryPolicy.NoRetry); } finally { @@ -86,4 +90,4 @@ namespace DotNetCore.CAP.AzureServiceBus } } } -} \ No newline at end of file +} diff --git a/src/DotNetCore.CAP.Dashboard/AwaitableInfo.cs b/src/DotNetCore.CAP.Dashboard/AwaitableInfo.cs deleted file mode 100644 index 5c494e9..0000000 --- a/src/DotNetCore.CAP.Dashboard/AwaitableInfo.cs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace Microsoft.Extensions.Internal -{ - internal struct AwaitableInfo - { - public Type AwaiterType { get; } - public PropertyInfo AwaiterIsCompletedProperty { get; } - public MethodInfo AwaiterGetResultMethod { get; } - public MethodInfo AwaiterOnCompletedMethod { get; } - public MethodInfo AwaiterUnsafeOnCompletedMethod { get; } - public Type ResultType { get; } - public MethodInfo GetAwaiterMethod { get; } - - public AwaitableInfo( - Type awaiterType, - PropertyInfo awaiterIsCompletedProperty, - MethodInfo awaiterGetResultMethod, - MethodInfo awaiterOnCompletedMethod, - MethodInfo awaiterUnsafeOnCompletedMethod, - Type resultType, - MethodInfo getAwaiterMethod) - { - AwaiterType = awaiterType; - AwaiterIsCompletedProperty = awaiterIsCompletedProperty; - AwaiterGetResultMethod = awaiterGetResultMethod; - AwaiterOnCompletedMethod = awaiterOnCompletedMethod; - AwaiterUnsafeOnCompletedMethod = awaiterUnsafeOnCompletedMethod; - ResultType = resultType; - GetAwaiterMethod = getAwaiterMethod; - } - - public static bool IsTypeAwaitable(Type type, out AwaitableInfo awaitableInfo) - { - // Based on Roslyn code: http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Shared/Extensions/ISymbolExtensions.cs,db4d48ba694b9347 - - // Awaitable must have method matching "object GetAwaiter()" - var getAwaiterMethod = type.GetRuntimeMethods().FirstOrDefault(m => - m.Name.Equals("GetAwaiter", StringComparison.OrdinalIgnoreCase) - && m.GetParameters().Length == 0 - && m.ReturnType != null); - if (getAwaiterMethod == null) - { - awaitableInfo = default(AwaitableInfo); - return false; - } - - var awaiterType = getAwaiterMethod.ReturnType; - - // Awaiter must have property matching "bool IsCompleted { get; }" - var isCompletedProperty = awaiterType.GetRuntimeProperties().FirstOrDefault(p => - p.Name.Equals("IsCompleted", StringComparison.OrdinalIgnoreCase) - && p.PropertyType == typeof(bool) - && p.GetMethod != null); - if (isCompletedProperty == null) - { - awaitableInfo = default(AwaitableInfo); - return false; - } - - // Awaiter must implement INotifyCompletion - var awaiterInterfaces = awaiterType.GetInterfaces(); - var implementsINotifyCompletion = awaiterInterfaces.Any(t => t == typeof(INotifyCompletion)); - if (!implementsINotifyCompletion) - { - awaitableInfo = default(AwaitableInfo); - return false; - } - - // INotifyCompletion supplies a method matching "void OnCompleted(Action action)" - var iNotifyCompletionMap = awaiterType - .GetTypeInfo() - .GetRuntimeInterfaceMap(typeof(INotifyCompletion)); - var onCompletedMethod = iNotifyCompletionMap.InterfaceMethods.Single(m => - m.Name.Equals("OnCompleted", StringComparison.OrdinalIgnoreCase) - && m.ReturnType == typeof(void) - && m.GetParameters().Length == 1 - && m.GetParameters()[0].ParameterType == typeof(Action)); - - // Awaiter optionally implements ICriticalNotifyCompletion - var implementsICriticalNotifyCompletion = - awaiterInterfaces.Any(t => t == typeof(ICriticalNotifyCompletion)); - MethodInfo unsafeOnCompletedMethod; - if (implementsICriticalNotifyCompletion) - { - // ICriticalNotifyCompletion supplies a method matching "void UnsafeOnCompleted(Action action)" - var iCriticalNotifyCompletionMap = awaiterType - .GetTypeInfo() - .GetRuntimeInterfaceMap(typeof(ICriticalNotifyCompletion)); - unsafeOnCompletedMethod = iCriticalNotifyCompletionMap.InterfaceMethods.Single(m => - m.Name.Equals("UnsafeOnCompleted", StringComparison.OrdinalIgnoreCase) - && m.ReturnType == typeof(void) - && m.GetParameters().Length == 1 - && m.GetParameters()[0].ParameterType == typeof(Action)); - } - else - { - unsafeOnCompletedMethod = null; - } - - // Awaiter must have method matching "void GetResult" or "T GetResult()" - var getResultMethod = awaiterType.GetRuntimeMethods().FirstOrDefault(m => - m.Name.Equals("GetResult") - && m.GetParameters().Length == 0); - if (getResultMethod == null) - { - awaitableInfo = default(AwaitableInfo); - return false; - } - - awaitableInfo = new AwaitableInfo( - awaiterType, - isCompletedProperty, - getResultMethod, - onCompletedMethod, - unsafeOnCompletedMethod, - getResultMethod.ReturnType, - getAwaiterMethod); - return true; - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/BatchCommandDispatcher.cs b/src/DotNetCore.CAP.Dashboard/BatchCommandDispatcher.cs deleted file mode 100644 index f711dee..0000000 --- a/src/DotNetCore.CAP.Dashboard/BatchCommandDispatcher.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Net; -using System.Threading.Tasks; - -namespace DotNetCore.CAP.Dashboard -{ - internal class BatchCommandDispatcher : IDashboardDispatcher - { - private readonly Action _command; - - public BatchCommandDispatcher(Action command) - { - _command = command; - } - - public async Task Dispatch(DashboardContext context) - { - var messageIds = await context.Request.GetFormValuesAsync("messages[]"); - if (messageIds.Count == 0) - { - context.Response.StatusCode = 422; - return; - } - - foreach (var messageId in messageIds) - { - var id = long.Parse(messageId); - _command(context, id); - } - - context.Response.StatusCode = (int) HttpStatusCode.NoContent; - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/CAP.BuilderExtension.cs b/src/DotNetCore.CAP.Dashboard/CAP.BuilderExtension.cs new file mode 100644 index 0000000..090cc5c --- /dev/null +++ b/src/DotNetCore.CAP.Dashboard/CAP.BuilderExtension.cs @@ -0,0 +1,140 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Reflection; +using System.Threading.Tasks; +using DotNetCore.CAP.Dashboard; +using DotNetCore.CAP.Dashboard.GatewayProxy; +using DotNetCore.CAP.Dashboard.NodeDiscovery; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Internal; + +// ReSharper disable once CheckNamespace +namespace DotNetCore.CAP +{ + public static class CapBuilderExtension + { + public static IApplicationBuilder UseCapDashboard(this IApplicationBuilder app) + { + if (app == null) + { + throw new ArgumentNullException(nameof(app)); + } + + var provider = app.ApplicationServices; + + var options = provider.GetService(); + if (options != null) + { + if (provider.GetService() != null) + { + app.UseMiddleware(); + } + + app.UseMiddleware(); + + app.Map(options.PathMatch + "/api", false, x => + { + + var builder = new RouteBuilder(x); + + var methods = typeof(RouteActionProvider).GetMethods(BindingFlags.Instance | BindingFlags.Public); + + foreach (var method in methods) + { + var executor = ObjectMethodExecutor.Create(method, typeof(RouteActionProvider).GetTypeInfo()); + + var getAttr = method.GetCustomAttribute(); + if (getAttr != null) + { + + builder.MapGet(getAttr.Template, async (request, response, data) => + { + if (!await Authentication(request.HttpContext, options)) + { + response.StatusCode = StatusCodes.Status401Unauthorized; + return; + } + + var actionProvider = new RouteActionProvider(request, response, data); + try + { + await executor.ExecuteAsync(actionProvider, null); + } + catch (Exception ex) + { + response.StatusCode = StatusCodes.Status500InternalServerError; + await response.WriteAsync(ex.Message); + } + }); + } + + var postAttr = method.GetCustomAttribute(); + if (postAttr != null) + { + builder.MapPost(postAttr.Template, async (request, response, data) => + { + if (!await Authentication(request.HttpContext, options)) + { + response.StatusCode = StatusCodes.Status401Unauthorized; + return; + } + + var actionProvider = new RouteActionProvider(request, response, data); + try + { + await executor.ExecuteAsync(actionProvider, null); + } + catch (Exception ex) + { + response.StatusCode = StatusCodes.Status500InternalServerError; + await response.WriteAsync(ex.Message); + } + }); + } + } + + var capRouter = builder.Build(); + + x.UseRouter(capRouter); + }); + } + + return app; + } + + internal static async Task Authentication(HttpContext context, DashboardOptions options) + { + if (options.UseAuth) + { + var result = await context.AuthenticateAsync(options.DefaultAuthenticationScheme); + + if (result.Succeeded && result.Principal != null) + { + context.User = result.Principal; + } + else + { + return false; + } + } + + var isAuthenticated = context.User?.Identity?.IsAuthenticated; + + if (isAuthenticated == false && options.UseChallengeOnAuth) + { + await context.ChallengeAsync(options.DefaultChallengeScheme); + + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/CAP.DashboardMiddleware.cs b/src/DotNetCore.CAP.Dashboard/CAP.DashboardMiddleware.cs deleted file mode 100644 index c8d5e92..0000000 --- a/src/DotNetCore.CAP.Dashboard/CAP.DashboardMiddleware.cs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Linq; -using System.Net; -using System.Threading.Tasks; -using DotNetCore.CAP.Dashboard; -using DotNetCore.CAP.Dashboard.GatewayProxy; -using DotNetCore.CAP.Dashboard.NodeDiscovery; -using DotNetCore.CAP.Persistence; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; - -// ReSharper disable once CheckNamespace -namespace DotNetCore.CAP -{ - public static class CapBuilderExtension - { - public static IApplicationBuilder UseCapDashboard(this IApplicationBuilder app) - { - if (app == null) - { - throw new ArgumentNullException(nameof(app)); - } - - CheckRequirement(app); - - var provider = app.ApplicationServices; - - if (provider.GetService() != null) - { - if (provider.GetService() != null) - { - app.UseMiddleware(); - } - - app.UseMiddleware(); - } - - return app; - } - - private static void CheckRequirement(IApplicationBuilder app) - { - var marker = app.ApplicationServices.GetService(); - if (marker == null) - { - throw new InvalidOperationException( - "AddCap() must be called on the service collection. eg: services.AddCap(...)"); - } - - var messageQueueMarker = app.ApplicationServices.GetService(); - if (messageQueueMarker == null) - { - throw new InvalidOperationException( - "You must be config used message queue provider at AddCap() options! eg: services.AddCap(options=>{ options.UseKafka(...) })"); - } - - var databaseMarker = app.ApplicationServices.GetService(); - if (databaseMarker == null) - { - throw new InvalidOperationException( - "You must be config used database provider at AddCap() options! eg: services.AddCap(options=>{ options.UseSqlServer(...) })"); - } - } - } - - sealed class CapStartupFilter : IStartupFilter - { - public Action Configure(Action next) - { - return app => - { - app.UseCapDashboard(); - - next(app); - }; - } - } - - public class DashboardMiddleware - { - private readonly RequestDelegate _next; - private readonly DashboardOptions _options; - private readonly RouteCollection _routes; - private readonly IDataStorage _storage; - - public DashboardMiddleware(RequestDelegate next, DashboardOptions options, IDataStorage storage, - RouteCollection routes) - { - _next = next ?? throw new ArgumentNullException(nameof(next)); - _options = options ?? throw new ArgumentNullException(nameof(options)); - _storage = storage ?? throw new ArgumentNullException(nameof(storage)); - _routes = routes ?? throw new ArgumentNullException(nameof(routes)); - } - - public async Task Invoke(HttpContext context) - { - if (!context.Request.Path.StartsWithSegments(_options.PathMatch, - out var matchedPath, out var remainingPath)) - { - await _next(context); - return; - } - - // Update the path - var path = context.Request.Path; - var pathBase = context.Request.PathBase; - context.Request.PathBase = pathBase.Add(matchedPath); - context.Request.Path = remainingPath; - - try - { - var dashboardContext = new CapDashboardContext(_storage, _options, context); - var findResult = _routes.FindDispatcher(context.Request.Path.Value); - - if (findResult == null) - { - await _next.Invoke(context); - return; - } - - foreach (var authorizationFilter in _options.Authorization) - { - var authenticateResult = await authorizationFilter.AuthorizeAsync(dashboardContext); - if (authenticateResult) continue; - - var isAuthenticated = context.User?.Identity?.IsAuthenticated; - - context.Response.StatusCode = isAuthenticated == true - ? (int)HttpStatusCode.Forbidden - : (int)HttpStatusCode.Unauthorized; - - return; - } - - dashboardContext.UriMatch = findResult.Item2; - - await findResult.Item1.Dispatch(dashboardContext); - } - finally - { - context.Request.PathBase = pathBase; - context.Request.Path = path; - } - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/CAP.DashboardOptions.cs b/src/DotNetCore.CAP.Dashboard/CAP.DashboardOptions.cs index e04ab24..a2e739e 100644 --- a/src/DotNetCore.CAP.Dashboard/CAP.DashboardOptions.cs +++ b/src/DotNetCore.CAP.Dashboard/CAP.DashboardOptions.cs @@ -1,34 +1,51 @@ // Copyright (c) .NET Core Community. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System.Collections.Generic; -using DotNetCore.CAP.Dashboard; - // ReSharper disable once CheckNamespace + namespace DotNetCore.CAP { public class DashboardOptions { public DashboardOptions() { - AppPath = "/"; PathMatch = "/cap"; - Authorization = new[] {new LocalRequestsOnlyAuthorizationFilter()}; StatsPollingInterval = 2000; } /// - /// The path for the Back To Site link. Set to in order to hide the Back To Site link. + /// When behind the proxy, specify the base path to allow spa call prefix. /// - public string AppPath { get; set; } + public string PathBase { get; set; } + /// + /// Path prefix to match from url path. + /// public string PathMatch { get; set; } - public IEnumerable Authorization { get; set; } - /// /// The interval the /stats endpoint should be polled with. /// public int StatsPollingInterval { get; set; } + + /// + /// Enable authentication on dashboard request. + /// + public bool UseAuth { get; set; } + + /// + /// Default scheme used for authentication. If no scheme is set, the DefaultScheme set up in AddAuthentication will be used. + /// + public string DefaultAuthenticationScheme { get; set; } + + /// + /// Enable authentication challenge on dashboard request. + /// + public bool UseChallengeOnAuth { get; set; } + + /// + /// Default scheme used for authentication challenge. If no scheme is set, the DefaultChallengeScheme set up in AddAuthentication will be used. + /// + public string DefaultChallengeScheme { get; set; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/CAP.DashboardOptionsExtensions.cs b/src/DotNetCore.CAP.Dashboard/CAP.DashboardOptionsExtensions.cs index 5a27d66..f959d8b 100644 --- a/src/DotNetCore.CAP.Dashboard/CAP.DashboardOptionsExtensions.cs +++ b/src/DotNetCore.CAP.Dashboard/CAP.DashboardOptionsExtensions.cs @@ -3,9 +3,9 @@ using System; using DotNetCore.CAP; -using DotNetCore.CAP.Dashboard; using DotNetCore.CAP.Dashboard.GatewayProxy; using DotNetCore.CAP.Dashboard.GatewayProxy.Requester; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; @@ -26,11 +26,22 @@ namespace DotNetCore.CAP _options?.Invoke(dashboardOptions); services.AddTransient(); services.AddSingleton(dashboardOptions); - services.AddSingleton(DashboardRoutes.Routes); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + } + } + sealed class CapStartupFilter : IStartupFilter + { + public Action Configure(Action next) + { + return app => + { + next(app); + + app.UseCapDashboard(); + }; } } } @@ -43,7 +54,7 @@ namespace Microsoft.Extensions.DependencyInjection { return capOptions.UseDashboard(opt => { }); } - + public static CapOptions UseDashboard(this CapOptions capOptions, Action options) { if (options == null) diff --git a/src/DotNetCore.CAP.Dashboard/CapCache.cs b/src/DotNetCore.CAP.Dashboard/CapCache.cs index 2aa827b..1282d65 100644 --- a/src/DotNetCore.CAP.Dashboard/CapCache.cs +++ b/src/DotNetCore.CAP.Dashboard/CapCache.cs @@ -1,7 +1,4 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -18,7 +15,7 @@ namespace DotNetCore.CAP.Dashboard /// // ReSharper disable once InheritdocConsiderUsage // ReSharper disable once InconsistentNaming - internal class Cache : IDisposable + public class Cache : IDisposable { #region Constructor and class members @@ -124,7 +121,7 @@ namespace DotNetCore.CAP.Dashboard private void RemoveByTimer(object state) { - Remove((K) state); + Remove((K)state); } #endregion @@ -254,8 +251,8 @@ namespace DotNetCore.CAP.Dashboard try { var removers = (from k in _cache.Keys.Cast() - where keyPattern(k) - select k).ToList(); + where keyPattern(k) + select k).ToList(); foreach (var workKey in removers) { @@ -357,7 +354,7 @@ namespace DotNetCore.CAP.Dashboard /// instance. /// The .Global member is lazy instanciated. /// - internal class CapCache : Cache + public class CapCache : Cache { #region Static Global Cache instance @@ -375,4 +372,4 @@ namespace DotNetCore.CAP.Dashboard } #endregion -} \ No newline at end of file +} diff --git a/src/DotNetCore.CAP.Dashboard/CoercedAwaitableInfo.cs b/src/DotNetCore.CAP.Dashboard/CoercedAwaitableInfo.cs deleted file mode 100644 index be1725f..0000000 --- a/src/DotNetCore.CAP.Dashboard/CoercedAwaitableInfo.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Linq.Expressions; -using Microsoft.Extensions.Internal; - -namespace DotNetCore.CAP.Dashboard -{ - internal struct CoercedAwaitableInfo - { - public AwaitableInfo AwaitableInfo { get; } - public Expression CoercerExpression { get; } - public Type CoercerResultType { get; } - public bool RequiresCoercion => CoercerExpression != null; - - public CoercedAwaitableInfo(AwaitableInfo awaitableInfo) - { - AwaitableInfo = awaitableInfo; - CoercerExpression = null; - CoercerResultType = null; - } - - public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType, - AwaitableInfo coercedAwaitableInfo) - { - CoercerExpression = coercerExpression; - CoercerResultType = coercerResultType; - AwaitableInfo = coercedAwaitableInfo; - } - - public static bool IsTypeAwaitable(Type type, out CoercedAwaitableInfo info) - { - if (AwaitableInfo.IsTypeAwaitable(type, out var directlyAwaitableInfo)) - { - info = new CoercedAwaitableInfo(directlyAwaitableInfo); - return true; - } - - info = default(CoercedAwaitableInfo); - return false; - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/CombinedResourceDispatcher.cs b/src/DotNetCore.CAP.Dashboard/CombinedResourceDispatcher.cs deleted file mode 100644 index e8e5f2a..0000000 --- a/src/DotNetCore.CAP.Dashboard/CombinedResourceDispatcher.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System.Reflection; -using System.Threading.Tasks; - -namespace DotNetCore.CAP.Dashboard -{ - internal class CombinedResourceDispatcher : EmbeddedResourceDispatcher - { - private readonly Assembly _assembly; - private readonly string _baseNamespace; - private readonly string[] _resourceNames; - - public CombinedResourceDispatcher( - string contentType, - Assembly assembly, - string baseNamespace, - params string[] resourceNames) : base(contentType, assembly, null) - { - _assembly = assembly; - _baseNamespace = baseNamespace; - _resourceNames = resourceNames; - } - - protected override async Task WriteResponse(DashboardResponse response) - { - foreach (var resourceName in _resourceNames) - { - await WriteResource( - response, - _assembly, - $"{_baseNamespace}.{resourceName}"); - } - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/CommandDispatcher.cs b/src/DotNetCore.CAP.Dashboard/CommandDispatcher.cs deleted file mode 100644 index 2959673..0000000 --- a/src/DotNetCore.CAP.Dashboard/CommandDispatcher.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Net; -using System.Threading.Tasks; - -namespace DotNetCore.CAP.Dashboard -{ - internal class CommandDispatcher : IDashboardDispatcher - { - private readonly Func _command; - - public CommandDispatcher(Func command) - { - _command = command; - } - - public Task Dispatch(DashboardContext context) - { - var request = context.Request; - var response = context.Response; - - if (!"POST".Equals(request.Method, StringComparison.OrdinalIgnoreCase)) - { - response.StatusCode = (int) HttpStatusCode.MethodNotAllowed; - return Task.FromResult(false); - } - - if (_command(context)) - { - response.StatusCode = (int) HttpStatusCode.NoContent; - } - else - { - response.StatusCode = 422; - } - - return Task.FromResult(true); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/Content/css/bootstrap.min.css b/src/DotNetCore.CAP.Dashboard/Content/css/bootstrap.min.css deleted file mode 100644 index e67a842..0000000 --- a/src/DotNetCore.CAP.Dashboard/Content/css/bootstrap.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(fonts/glyphicons-halflings-regular/eot);src:url(fonts/glyphicons-halflings-regular/eot?#iefix) format('embedded-opentype'),url(fonts/glyphicons-halflings-regular/woff2) format('woff2'),url(fonts/glyphicons-halflings-regular/woff) format('woff'),url(fonts/glyphicons-halflings-regular/ttf) format('truetype'),url(fonts/glyphicons-halflings-regular/svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/Content/css/cap.css b/src/DotNetCore.CAP.Dashboard/Content/css/cap.css deleted file mode 100644 index dbcc0a5..0000000 --- a/src/DotNetCore.CAP.Dashboard/Content/css/cap.css +++ /dev/null @@ -1,460 +0,0 @@ -/* Sticky footer styles --------------------------------------------------- */ - -html, body { - height: 100%; - /* The html and body elements cannot have any padding or margin. */ -} - -body { - /* 75px to make the container go all the way to the bottom of the topbar */ - padding-top: 75px; -} - -/* Wrapper for page content to push down footer */ - -#wrap { - height: auto !important; - height: 100%; - /* Negative indent footer by its height */ - margin: 0 auto -60px; - min-height: 100%; - /* Pad bottom by footer height */ - padding: 0 0 60px; -} - -/* Set the fixed height of the footer here */ - -#footer { background-color: #f5f5f5; } - - -/* Custom page CSS --------------------------------------------------- */ - -.container .credit { margin: 20px 0; } - -.page-header { - margin-top: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.btn-death { - background-color: #777; - border-color: #666; - color: #fff; -} - -.btn-death:hover { - background-color: #666; - border-color: #555; - color: #fff; -} - -.list-group .list-group-item .glyphicon { margin-right: 3px; } - -.breadcrumb { - background-color: inherit; - margin-bottom: 10px; - padding: 0; -} - -.btn-toolbar-label { - display: inline-block; - margin-left: 5px; - padding: 7px 0; - vertical-align: middle; -} - -.btn-toolbar-label-sm { padding: 5px 0; } - -.btn-toolbar-spacer { - display: inline-block; - height: 1px; - width: 5px; -} - -a:hover .label-hover { - background-color: #2a6496 !important; - color: #fff !important; -} - -.expander { cursor: pointer; } - -.expandable { display: none; } - -.table-inner { - font-size: 90%; - margin-bottom: 7px; -} - -.min-width { - white-space: nowrap; - width: 1%; -} - -.align-right { text-align: right; } - -.table > tbody > tr.hover:hover > td, .table > tbody > tr.hover:hover > th { background-color: #f9f9f9; } - -.table > tbody > tr.highlight > td, .table > tbody > tr.highlight > th { - background-color: #fcf8e3; - border-color: #fbeed5; -} - -.table > tbody > tr.highlight:hover > td, .table > tbody > tr.highlight:hover > th { - background-color: #f6f2dd; - border-color: #f5e8ce; -} - -.word-break { word-break: break-all; } - -/* Statistics widget --------------------------------------------------- */ - -#stats .list-group-item { - background-color: #f8f8f8; - border-color: #e7e7e7; -} - -#stats a.list-group-item { color: #777; } - -#stats a.list-group-item:hover, -#stats a.list-group-item:focus { color: #333; } - -#stats .list-group-item.active, -#stats .list-group-item.active:hover, -#stats .list-group-item.active:focus { - background-color: #e7e7e7; - border-color: #e7e7e7; - color: #555; -} - -.table td.failed-job-details { - background-color: #f5f5f5; - border-top: none; - padding-bottom: 0; - padding-top: 0; -} - -.obsolete-data, .obsolete-data a, .obsolete-data pre, .obsolete-data .label { color: #999; } - -.obsolete-data pre, .obsolete-data .label { background-color: #f5f5f5; } - -.obsolete-data .glyphicon-question-sign { - color: #999; - font-size: 80%; -} - -.stack-trace { - border: none; - padding: 10px; -} - -.st-type { font-weight: bold; } - -.st-param-name { color: #666; } - -.st-file { color: #999; } - -.st-method { - color: #00008B; - font-weight: bold; -} - -.st-line { color: #8B008B; } - -.width-200 { width: 200px; } - -.btn-toolbar-top { margin-bottom: 10px; } - -.paginator .btn { color: #428bca; } - -.paginator .btn.active { color: #333; } - -/* Job Snippet styles */ - -.job-snippet { - -ms-border-radius: 4px; - background-color: #f5f5f5; - border-radius: 4px; - display: table; - margin-bottom: 20px; - padding: 15px; - width: 100%; -} - -.job-snippet > * { - display: table-cell; - vertical-align: top; -} - -.job-snippet-code { vertical-align: top; } - -.job-snippet-code pre { - -ms-border-radius: 0; - background: inherit; - border: none; - border-radius: 0; - font-size: 14px; - margin: 0; - padding: 0; -} - -.job-snippet-code code { - background-color: #f5f5f5; - color: black; - display: block; -} - -.job-snippet-code pre .comment { color: rgb(0, 128, 0); } - -.job-snippet-code pre .keyword { color: rgb(0, 0, 255); } - -.job-snippet-code pre .string { color: rgb(163, 21, 21); } - -.job-snippet-code pre .type { color: rgb(43, 145, 175); } - -.job-snippet-code pre .xmldoc { color: rgb(128, 128, 128); } - -.job-snippet-properties { - max-width: 200px; - padding-left: 5px; -} - -.job-snippet-properties dl { margin: 0; } - -.job-snippet-properties dl dt { - color: #999; - font-weight: normal; - text-shadow: 0 1px white; -} - -.job-snippet-properties dl dd { - margin-bottom: 5px; - margin-left: 0; -} - -.job-snippet-properties pre { - -ms-box-shadow: none; - -webkit-box-shadow: none; - background-color: white; - border: none; - margin: 0; - padding: 2px 4px; -} - -.job-snippet-properties code { color: black; } - -.state-card { - -ms-border-radius: 3px; - background-color: #fff; - border: 1px solid #e5e5e5; - border-radius: 3px; - display: block; - margin-bottom: 7px; - padding: 12px; - position: relative; -} - -.state-card-title { margin-bottom: 0; } - -.state-card-title .pull-right { margin-top: 3px; } - -.state-card-text { - margin-bottom: 0; - margin-top: 5px; -} - -.state-card h4 { margin-top: 0; } - -.state-card-body { - -ms-border-bottom-left-radius: 3px; - -ms-border-bottom-right-radius: 3px; - background-color: #f5f5f5; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; - margin: 10px -12px -12px -12px; - padding: 10px; -} - -.state-card-body dl { - margin-bottom: 0; - margin-top: 5px; -} - -.state-card-body pre { - background: transparent; - padding: 0; - white-space: pre-wrap; /* CSS 3 */ - word-wrap: break-word; /* Internet Explorer 5.5+ */ -} - -.state-card-body .stack-trace { - background-color: transparent; - margin-bottom: 0; - padding: 0 20px; -} - -.state-card-body .exception-type { margin-top: 0; } - -/* Job History styles */ - -.job-history { - margin-bottom: 10px; - opacity: 0.8; -} - -.job-history.job-history-current { opacity: 1.0; } - -.job-history-heading { - -ms-border-top-left-radius: 4px; - -ms-border-top-right-radius: 4px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - color: #666; - padding: 5px 10px; -} - -.job-history-body { - background-color: #f5f5f5; - padding: 10px; -} - -.job-history-title { - margin-bottom: 2px; - margin-top: 0; -} - -.job-history dl { - margin-bottom: 5px; - margin-top: 5px; -} - -.job-history .stack-trace { - background-color: transparent; - margin-bottom: 5px; - padding: 0 20px; -} - -.job-history .exception-type { margin-top: 0; } - -.job-history-current .job-history-heading, -.job-history-current small { color: white; } - -a.job-method { color: inherit; } - -.list-group .glyphicon { top: 2px; } - -span.metric { - -moz-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out; - -ms-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out; - -o-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out; - -webkit-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out; - background-color: transparent; - border: solid 1px; - border-radius: 10px; - display: inline-block; - font-size: 12px; - line-height: 1; - min-width: 10px; - padding: 2px 6px; - text-align: center; - transition: color .1s ease-out, background .1s ease-out, border .1s ease-out; - vertical-align: baseline; - white-space: nowrap; -} - -span.metric.highlighted { - color: #fff !important; - font-weight: bold; -} - -span.metric-default { - border-color: #777; - color: #777; -} - -span.metric-default.highlighted { background-color: #777; } - -div.metric { - -ms-border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - border: solid 1px transparent; - border-radius: 4px; - box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - margin-bottom: 20px; - transition: color .1s ease-out, background .1s ease-out, border .1s ease-out; -} - -div.metric .metric-body { - font-size: 26px; - padding: 15px 15px 0; - text-align: center; -} - -div.metric .metric-description { - padding: 0 15px 15px; - text-align: center; -} - -div.metric.metric-default { border-color: #ddd; } - -div.metric-info, -span.metric-info { - border-color: #5bc0de; - color: #5bc0de; -} - -span.metric-info.highlighted { background-color: #5bc0de; } - -div.metric-warning, -span.metric-warning { - border-color: #f0ad4e; - color: #f0ad4e; -} - -span.metric-warning.highlighted { background-color: #f0ad4e; } - -div.metric-success, -span.metric-success { - border-color: #5cb85c; - color: #5cb85c; -} - -span.metric-success.highlighted { background-color: #5cb85c; } - -div.metric-danger, -span.metric-danger { - border-color: #d9534f; - color: #d9534f; -} - -span.metric-danger.highlighted { background-color: #d9534f; } - -span.metric-null, -div.metric-null { display: none; } - -@media (min-width: 992px) { - #stats { - position: fixed; - width: 220px - } -} - -@media (min-width: 1200px) { - #stats { width: 262.5px; } -} - -.subscribe-table td { vertical-align: middle !important; } - -.subscribe-table td[rowspan] { font-weight: bold; } - -#legend { - background: rgba(173, 169, 169, 0.13); - color: #000; - position: absolute; - right: 20px; - top: 110px; -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/Content/css/jsonview.min.css b/src/DotNetCore.CAP.Dashboard/Content/css/jsonview.min.css deleted file mode 100644 index 97805e3..0000000 --- a/src/DotNetCore.CAP.Dashboard/Content/css/jsonview.min.css +++ /dev/null @@ -1 +0,0 @@ -@charset "UTF-8";.jsonview{font-family:monospace;font-size:1.1em;white-space:pre-wrap}.jsonview .prop{font-weight:700;text-decoration:none;color:#000}.jsonview .null,.jsonview .undefined{color:red}.jsonview .bool,.jsonview .num{color:#00f}.jsonview .string{color:green;white-space:pre-wrap}.jsonview .string.multiline{display:inline-block;vertical-align:text-top}.jsonview .collapser{position:absolute;left:-1em;cursor:pointer}.jsonview .collapsible{transition:height 1.2s;transition:width 1.2s}.jsonview .collapsible.collapsed{height:.8em;width:1em;display:inline-block;overflow:hidden;margin:0}.jsonview .collapsible.collapsed:before{content:"…";width:1em;margin-left:.2em}.jsonview .collapser.collapsed{transform:rotate(0)}.jsonview .q{display:inline-block;width:0;color:transparent}.jsonview li{position:relative}.jsonview ul{list-style:none;margin:0 0 0 2em;padding:0}.jsonview h1{font-size:1.2em} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/Content/css/rickshaw.min.css b/src/DotNetCore.CAP.Dashboard/Content/css/rickshaw.min.css deleted file mode 100644 index 6958078..0000000 --- a/src/DotNetCore.CAP.Dashboard/Content/css/rickshaw.min.css +++ /dev/null @@ -1 +0,0 @@ -.rickshaw_graph .detail .item.left,.rickshaw_graph .detail .x_label.left{left:0}.rickshaw_graph .detail .item.right,.rickshaw_graph .detail .x_label.right{right:0}.rickshaw_graph .detail .item,.rickshaw_graph .detail .x_label,.rickshaw_graph .x_tick .title{font-family:Arial,sans-serif;font-size:12px;white-space:nowrap}.rickshaw_graph .detail{pointer-events:none;position:absolute;top:0;z-index:2;background:rgba(0,0,0,.1);bottom:0;width:1px;transition:opacity .25s linear;-moz-transition:opacity .25s linear;-o-transition:opacity .25s linear;-webkit-transition:opacity .25s linear}.rickshaw_graph .detail.inactive{opacity:0}.rickshaw_graph .detail .item.active{opacity:1}.rickshaw_graph .detail .x_label{border-radius:3px;padding:6px;opacity:.5;border:1px solid #e0e0e0;position:absolute;background:#fff}.rickshaw_graph .detail .item{position:absolute;z-index:2;border-radius:3px;padding:.25em;opacity:0;background:rgba(0,0,0,.4);color:#fff;border:1px solid rgba(0,0,0,.4);margin-left:1em;margin-right:1em;margin-top:-1em}.rickshaw_graph .detail .item.active{background:rgba(0,0,0,.8)}.rickshaw_graph .detail .item:after{position:absolute;display:block;width:0;height:0;content:"";border:5px solid transparent}.rickshaw_graph .detail .item.left:after{top:1em;left:-5px;margin-top:-5px;border-right-color:rgba(0,0,0,.8);border-left-width:0}.rickshaw_graph .detail .item.right:after{top:1em;right:-5px;margin-top:-5px;border-left-color:rgba(0,0,0,.8);border-right-width:0}.rickshaw_graph .detail .dot{width:4px;height:4px;margin-left:-3px;margin-top:-3.5px;border-radius:5px;position:absolute;box-shadow:0 0 2px rgba(0,0,0,.6);box-sizing:content-box;-moz-box-sizing:content-box;background:#fff;border-width:2px;border-style:solid;display:none;background-clip:padding-box}.rickshaw_graph .detail .dot.active{display:block}.rickshaw_graph{position:relative}.rickshaw_graph svg{display:block;overflow:hidden}.rickshaw_graph .x_tick{position:absolute;top:0;bottom:0;width:0;border-left:1px dotted rgba(0,0,0,.2);pointer-events:none}.rickshaw_graph .x_tick .title{position:absolute;opacity:.5;margin-left:3px;bottom:1px}.rickshaw_annotation_timeline{height:1px;border-top:1px solid #e0e0e0;margin-top:10px;position:relative}.rickshaw_annotation_timeline .annotation{position:absolute;height:6px;width:6px;margin-left:-2px;top:-3px;border-radius:5px;background-color:rgba(0,0,0,.25)}.rickshaw_graph .annotation_line{position:absolute;top:0;bottom:-6px;width:0;border-left:2px solid rgba(0,0,0,.3);display:none}.rickshaw_graph .annotation_line.active{display:block}.rickshaw_graph .annotation_range{background:rgba(0,0,0,.1);display:none;position:absolute;top:0;bottom:-6px}.rickshaw_graph .annotation_range.active{display:block}.rickshaw_graph .annotation_range.active.offscreen{display:none}.rickshaw_annotation_timeline .annotation .content{background:#fff;color:#000;opacity:.9;box-shadow:0 0 2px rgba(0,0,0,.8);border-radius:3px;position:relative;z-index:20;font-size:12px;padding:6px 8px 8px;top:18px;left:-11px;width:160px;display:none;cursor:pointer}.rickshaw_annotation_timeline .annotation .content:before{content:"\25b2";position:absolute;top:-11px;color:#fff;text-shadow:0 -1px 1px rgba(0,0,0,.8)}.rickshaw_annotation_timeline .annotation.active,.rickshaw_annotation_timeline .annotation:hover{background-color:rgba(0,0,0,.8);cursor:none}.rickshaw_annotation_timeline .annotation .content:hover{z-index:50}.rickshaw_annotation_timeline .annotation.active .content{display:block}.rickshaw_annotation_timeline .annotation:hover .content{display:block;z-index:50}.rickshaw_graph .x_axis_d3,.rickshaw_graph .y_axis{fill:none}.rickshaw_graph .x_ticks_d3 .tick,.rickshaw_graph .y_ticks .tick line{stroke:rgba(0,0,0,.16);stroke-width:2px;shape-rendering:crisp-edges;pointer-events:none}.rickshaw_graph .x_grid_d3 .tick,.rickshaw_graph .y_grid .tick{z-index:-1;stroke:rgba(0,0,0,.2);stroke-width:1px;stroke-dasharray:1 1}.rickshaw_graph .y_grid .tick[data-y-value="0"]{stroke-dasharray:1 0}.rickshaw_graph .x_grid_d3 path,.rickshaw_graph .y_grid path{fill:none;stroke:none}.rickshaw_graph .x_ticks_d3 path,.rickshaw_graph .y_ticks path{fill:none;stroke:grey}.rickshaw_graph .x_ticks_d3 text,.rickshaw_graph .y_ticks text{opacity:.5;font-size:12px;pointer-events:none}.rickshaw_graph .x_tick.glow .title,.rickshaw_graph .y_ticks.glow text{fill:#000;color:#000;text-shadow:-1px 1px 0 rgba(255,255,255,.1),1px -1px 0 rgba(255,255,255,.1),1px 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1),0 -1px 0 rgba(255,255,255,.1),1px 0 0 rgba(255,255,255,.1),-1px 0 0 rgba(255,255,255,.1),-1px -1px 0 rgba(255,255,255,.1)}.rickshaw_graph .x_tick.inverse .title,.rickshaw_graph .y_ticks.inverse text{fill:#fff;color:#fff;text-shadow:-1px 1px 0 rgba(0,0,0,.8),1px -1px 0 rgba(0,0,0,.8),1px 1px 0 rgba(0,0,0,.8),0 1px 0 rgba(0,0,0,.8),0 -1px 0 rgba(0,0,0,.8),1px 0 0 rgba(0,0,0,.8),-1px 0 0 rgba(0,0,0,.8),-1px -1px 0 rgba(0,0,0,.8)}.rickshaw_legend{font-family:Arial;font-size:12px;color:#fff;background:#404040;display:inline-block;padding:12px 5px;border-radius:2px;position:relative}.rickshaw_legend:hover{z-index:10}.rickshaw_legend .swatch{width:10px;height:10px;border:1px solid rgba(0,0,0,.2)}.rickshaw_legend .line{clear:both;line-height:140%;padding-right:15px}.rickshaw_legend .line .swatch{display:inline-block;margin-right:3px;border-radius:2px}.rickshaw_legend .label{margin:0;white-space:nowrap;display:inline;font-size:inherit;background-color:transparent;color:inherit;font-weight:400;line-height:normal;padding:0;text-shadow:none}.rickshaw_legend .action:hover{opacity:.6}.rickshaw_legend .action{margin-right:.2em;opacity:.2;cursor:pointer;font-size:14px}.rickshaw_legend .line.disabled{opacity:.4}.rickshaw_legend ul{list-style-type:none;padding:0;margin:2px;cursor:pointer}.rickshaw_legend li{padding:0 0 0 2px;min-width:80px;white-space:nowrap}.rickshaw_legend li:hover{background:rgba(255,255,255,.08);border-radius:3px}.rickshaw_legend li:active{background:rgba(255,255,255,.2);border-radius:3px} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.eot b/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.eot deleted file mode 100644 index b93a495..0000000 Binary files a/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.eot and /dev/null differ diff --git a/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.svg b/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.svg deleted file mode 100644 index 94fb549..0000000 --- a/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.svg +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.ttf b/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.ttf deleted file mode 100644 index 1413fc6..0000000 Binary files a/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.ttf and /dev/null differ diff --git a/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.woff b/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.woff deleted file mode 100644 index 9e61285..0000000 Binary files a/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.woff and /dev/null differ diff --git a/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.woff2 b/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.woff2 deleted file mode 100644 index 64539b5..0000000 Binary files a/src/DotNetCore.CAP.Dashboard/Content/fonts/glyphicons-halflings-regular.woff2 and /dev/null differ diff --git a/src/DotNetCore.CAP.Dashboard/Content/js/bootstrap.min.js b/src/DotNetCore.CAP.Dashboard/Content/js/bootstrap.min.js deleted file mode 100644 index 133aeec..0000000 --- a/src/DotNetCore.CAP.Dashboard/Content/js/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under the MIT license - */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/Content/js/cap.js b/src/DotNetCore.CAP.Dashboard/Content/js/cap.js deleted file mode 100644 index 674e88e..0000000 --- a/src/DotNetCore.CAP.Dashboard/Content/js/cap.js +++ /dev/null @@ -1,656 +0,0 @@ -(function(cap) { - cap.config = { - pollInterval: $("#capConfig").data("pollinterval"), - pollUrl: $("#capConfig").data("pollurl"), - locale: document.documentElement.lang - }; - - cap.Metrics = (function() { - function Metrics() { - this._metrics = {}; - } - - Metrics.prototype.addElement = function(name, element) { - if (!(name in this._metrics)) { - this._metrics[name] = []; - } - - this._metrics[name].push(element); - }; - - Metrics.prototype.getElements = function(name) { - if (!(name in this._metrics)) { - return []; - } - - return this._metrics[name]; - }; - - Metrics.prototype.getNames = function() { - const result = []; - const metrics = this._metrics; - - for (let name in metrics) { - if (metrics.hasOwnProperty(name)) { - result.push(name); - } - } - - return result; - }; - - return Metrics; - })(); - - var BaseGraph = function() { - this.height = 200; - }; - - BaseGraph.prototype.update = function() { - const graph = this._graph; - - const width = $(graph.element).innerWidth(); - if (width !== graph.width) { - graph.configure({ - width: width, - height: this.height - }); - } - - graph.update(); - }; - - BaseGraph.prototype._initGraph = function(element, settings, xSettings, ySettings) { - - const graph = this._graph = new Rickshaw.Graph($.extend({ - element: element, - width: $(element).innerWidth(), - height: this.height, - interpolation: "linear", - stroke: true - }, - settings)); - - this._hoverDetail = new Rickshaw.Graph.HoverDetail({ - graph: graph, - yFormatter: function(y) { return Math.floor(y); }, - xFormatter: function(x) { return moment(new Date(x * 1000)).format("LLLL"); } - }); - - if (xSettings) { - this._xAxis = new Rickshaw.Graph.Axis.Time($.extend({ - graph: graph, - timeFixture: new Rickshaw.Fixtures.Time.Local() - }, - xSettings)); - - const legend = new Rickshaw.Graph.Legend({ - element: document.querySelector("#legend"), - graph: graph - }); - } - - if (ySettings) { - this._yAxis = new Rickshaw.Graph.Axis.Y($.extend({ - graph: graph, - tickFormat: Rickshaw.Fixtures.Number.formatKMBT - }, - ySettings)); - } - - graph.render(); - }; - - cap.RealtimeGraph = (function() { - function RealtimeGraph(element, - pubSucceeded, - pubFailed, - pubSucceededStr, - pubFailedStr, - recSucceeded, - recFailed, - recSucceededStr, - recFailedStr - ) { - this._pubSucceeded = pubSucceeded; - this._pubSucceededStr = pubSucceededStr; - - this._pubFailed = pubFailed; - this._pubFailedStr = pubFailedStr; - - this._recSucceeded = recSucceeded; - this._recSucceededStr = recSucceededStr; - - this._recFailed = recFailed; - this._recFailedStr = recFailedStr; - - this._initGraph(element, - { - renderer: "bar", - series: new Rickshaw.Series.FixedDuration([ - { - name: pubSucceededStr, - color: "#33cc33" - }, { - name: recSucceededStr, - color: "#3333cc" - }, { - name: pubFailedStr, - color: "#ff3300" - }, { - name: recFailedStr, - color: "#ff3399" - } - ], - undefined, - { timeInterval: 2000, maxDataPoints: 100 } - ) - }, - null, - {}); - } - - RealtimeGraph.prototype = Object.create(BaseGraph.prototype); - - RealtimeGraph.prototype.appendHistory = function(statistics) { - const newPubSucceeded = parseInt(statistics["published_succeeded:count"].intValue); - const newPubFailed = parseInt(statistics["published_failed:count"].intValue); - - const newRecSucceeded = parseInt(statistics["received_succeeded:count"].intValue); - const newRecFailed = parseInt(statistics["received_failed:count"].intValue); - - if (this._pubSucceeded !== null && - this._pubFailed !== null && - this._recSucceeded !== null && - this._recFailed !== null - ) { - const pubSucceeded = newPubSucceeded - this._pubSucceeded; - const pubFailed = newPubFailed - this._pubFailed; - - const recSucceeded = newRecSucceeded - this._recSucceeded; - const recFailed = newRecFailed - this._recFailed; - - const dataObj = {}; - dataObj[this._pubFailedStr] = pubFailed; - dataObj[this._pubSucceededStr] = pubSucceeded; - dataObj[this._recFailedStr] = recFailed; - dataObj[this._recSucceededStr] = recSucceeded; - - this._graph.series.addData(dataObj); - this._graph.render(); - } - - this._pubSucceeded = newPubSucceeded; - this._pubFailed = newPubFailed; - - this._recSucceeded = newRecSucceeded; - this._recFailed = newRecFailed; - }; - - return RealtimeGraph; - })(); - - cap.HistoryGraph = (function() { - function HistoryGraph(element, - pubSucceeded, - pubFailed, - pubSucceededStr, - pubFailedStr, - recSucceeded, - recFailed, - recSucceededStr, - recFailedStr) { - this._initGraph(element, - { - renderer: "area", - series: [ - { - color: "#33cc33", - data: pubSucceeded, - name: pubSucceededStr - }, { - color: "#3333cc", - data: recSucceeded, - name: recSucceededStr - }, { - color: "#ff3300", - data: pubFailed, - name: pubFailedStr - }, { - color: "#ff3399", - data: recFailed, - name: recFailedStr - } - ] - }, - {}, - { ticksTreatment: "glow" }); - } - - HistoryGraph.prototype = Object.create(BaseGraph.prototype); - - return HistoryGraph; - })(); - - cap.StatisticsPoller = (function() { - function StatisticsPoller(metricsCallback, statisticsUrl, pollInterval) { - this._metricsCallback = metricsCallback; - this._listeners = []; - this._statisticsUrl = statisticsUrl; - this._pollInterval = pollInterval; - this._intervalId = null; - } - - StatisticsPoller.prototype.start = function() { - var self = this; - - const intervalFunc = function() { - try { - $.post(self._statisticsUrl, - { metrics: self._metricsCallback() }, - function(data) { - self._notifyListeners(data); - }); - } catch (e) { - console.log(e); - } - }; - - this._intervalId = setInterval(intervalFunc, this._pollInterval); - }; - - StatisticsPoller.prototype.stop = function() { - if (this._intervalId !== null) { - clearInterval(this._intervalId); - this._intervalId = null; - } - }; - - StatisticsPoller.prototype.addListener = function(listener) { - this._listeners.push(listener); - }; - - StatisticsPoller.prototype._notifyListeners = function(statistics) { - const length = this._listeners.length; - var i; - - for (i = 0; i < length; i++) { - this._listeners[i](statistics); - } - }; - - return StatisticsPoller; - })(); - - cap.Page = (function() { - function Page(config) { - this._metrics = new cap.Metrics(); - - var self = this; - this._poller = new cap.StatisticsPoller( - function() { return self._metrics.getNames(); }, - config.pollUrl, - config.pollInterval); - - this._initialize(config.locale); - - this.realtimeGraph = this._createRealtimeGraph("realtimeGraph"); - this.historyGraph = this._createHistoryGraph("historyGraph"); - - this._poller.start(); - }; - - Page.prototype._createRealtimeGraph = function(elementId) { - const realtimeElement = document.getElementById(elementId); - if (realtimeElement) { - const pubSucceeded = parseInt($(realtimeElement).data("published-succeeded")); - const pubFailed = parseInt($(realtimeElement).data("published-failed")); - const pubSucceededStr = $(realtimeElement).data("published-succeeded-string"); - const pubFailedStr = $(realtimeElement).data("published-failed-string"); - - const recSucceeded = parseInt($(realtimeElement).data("received-succeeded")); - const recFailed = parseInt($(realtimeElement).data("received-failed")); - const recSucceededStr = $(realtimeElement).data("received-succeeded-string"); - const recFailedStr = $(realtimeElement).data("received-failed-string"); - - var realtimeGraph = new Cap.RealtimeGraph(realtimeElement, - pubSucceeded, - pubFailed, - pubSucceededStr, - pubFailedStr, - recSucceeded, - recFailed, - recSucceededStr, - recFailedStr - ); - - this._poller.addListener(function(data) { - realtimeGraph.appendHistory(data); - }); - - $(window).resize(function() { - realtimeGraph.update(); - }); - - return realtimeGraph; - } - - return null; - }; - - Page.prototype._createHistoryGraph = function(elementId) { - const historyElement = document.getElementById(elementId); - if (historyElement) { - const createSeries = function(obj) { - const series = []; - for (let date in obj) { - if (obj.hasOwnProperty(date)) { - const value = obj[date]; - const point = { x: Date.parse(date) / 1000, y: value }; - series.unshift(point); - } - } - return series; - }; - - const publishedSucceeded = createSeries($(historyElement).data("published-succeeded")); - const publishedFailed = createSeries($(historyElement).data("published-failed")); - const publishedSucceededStr = $(historyElement).data("published-succeeded-string"); - const publishedFailedStr = $(historyElement).data("published-failed-string"); - - const receivedSucceeded = createSeries($(historyElement).data("received-succeeded")); - const receivedFailed = createSeries($(historyElement).data("received-failed")); - const receivedSucceededStr = $(historyElement).data("received-succeeded-string"); - const receivedFailedStr = $(historyElement).data("received-failed-string"); - - var historyGraph = new Cap.HistoryGraph(historyElement, - publishedSucceeded, - publishedFailed, - publishedSucceededStr, - publishedFailedStr, - receivedSucceeded, - receivedFailed, - receivedSucceededStr, - receivedFailedStr - ); - - $(window).resize(function() { - historyGraph.update(); - }); - - return historyGraph; - } - - return null; - }; - - Page.prototype._initialize = function(locale) { - moment.locale(locale); - const updateRelativeDates = function() { - $("*[data-moment]").each(function() { - const $this = $(this); - const timestamp = $this.data("moment"); - - if (timestamp) { - const time = moment(timestamp, "X"); - $this.html(time.fromNow()) - .attr("title", time.format("llll")) - .attr("data-container", "body"); - } - }); - - $("*[data-moment-title]").each(function() { - const $this = $(this); - const timestamp = $this.data("moment-title"); - - if (timestamp) { - const time = moment(timestamp, "X"); - $this.prop("title", time.format("llll")) - .attr("data-container", "body"); - } - }); - - $("*[data-moment-local]").each(function() { - const $this = $(this); - const timestamp = $this.data("moment-local"); - - if (timestamp) { - const time = moment(timestamp, "X"); - $this.html(time.format("l LTS")); - } - }); - }; - - updateRelativeDates(); - setInterval(updateRelativeDates, 30 * 1000); - - $("*[title]").tooltip(); - - var self = this; - $("*[data-metric]").each(function() { - const name = $(this).data("metric"); - self._metrics.addElement(name, this); - }); - - this._poller.addListener(function(metrics) { - for (let name in metrics) { - const elements = self._metrics.getElements(name); - for (let i = 0; i < elements.length; i++) { - const metric = metrics[name]; - const metricClass = metric ? `metric-${metric.style}` : "metric-null"; - const highlighted = metric && metric.highlighted ? "highlighted" : null; - const value = metric ? metric.value : null; - - $(elements[i]) - .text(value) - .closest(".metric") - .removeClass() - .addClass(["metric", metricClass, highlighted].join(" ")); - } - } - }); - - $(document).on("click", - "*[data-ajax]", - function(e) { - var $this = $(this); - const confirmText = $this.data("confirm"); - - if (!confirmText || confirm(confirmText)) { - $this.prop("disabled"); - var loadingDelay = setTimeout(function() { - $this.button("loading"); - }, - 100); - - $.post($this.data("ajax"), - function() { - clearTimeout(loadingDelay); - window.location.reload(); - }); - } - - e.preventDefault(); - }); - - $(document).on("click", - ".expander", - function(e) { - var $expander = $(this), - $expandable = $expander.closest("tr").next().find(".expandable"); - - if (!$expandable.is(":visible")) { - $expander.text("Less details..."); - } - - $expandable.slideToggle( - 150, - function() { - if (!$expandable.is(":visible")) { - $expander.text("More details..."); - } - }); - e.preventDefault(); - }); - - $(".js-jobs-list").each(function() { - var container = this; - - var selectRow = function(row, isSelected) { - const $checkbox = $(".js-jobs-list-checkbox", row); - if ($checkbox.length > 0) { - $checkbox.prop("checked", isSelected); - $(row).toggleClass("highlight", isSelected); - } - }; - - var toggleRowSelection = function(row) { - const $checkbox = $(".js-jobs-list-checkbox", row); - if ($checkbox.length > 0) { - const isSelected = $checkbox.is(":checked"); - selectRow(row, !isSelected); - } - }; - - var setListState = function(state) { - $(".js-jobs-list-select-all", container) - .prop("checked", state === "all-selected") - .prop("indeterminate", state === "some-selected"); - - $(".js-jobs-list-command", container) - .prop("disabled", state === "none-selected"); - }; - - var updateListState = function() { - const selectedRows = $(".js-jobs-list-checkbox", container).map(function() { - return $(this).prop("checked"); - }).get(); - - var state = "none-selected"; - - if (selectedRows.length > 0) { - state = "some-selected"; - - if ($.inArray(false, selectedRows) === -1) { - state = "all-selected"; - } else if ($.inArray(true, selectedRows) === -1) { - state = "none-selected"; - } - } - - setListState(state); - }; - - $(this).on("click", - ".js-jobs-list-checkbox", - function(e) { - selectRow( - $(this).closest(".js-jobs-list-row").first(), - $(this).is(":checked")); - - updateListState(); - - e.stopPropagation(); - }); - - $(this).on("click", - ".js-jobs-list-row", - function(e) { - if ($(e.target).is("a")) return; - - toggleRowSelection(this); - updateListState(); - }); - - $(this).on("click", - ".js-jobs-list-select-all", - function() { - var selectRows = $(this).is(":checked"); - - $(".js-jobs-list-row", container).each(function() { - selectRow(this, selectRows); - }); - - updateListState(); - }); - - $(this).on("click", - ".js-jobs-list-command", - function(e) { - var $this = $(this); - const confirmText = $this.data("confirm"); - - const jobs = $("input[name='messages[]']:checked", container).map(function() { - return $(this).val(); - }).get(); - - if (!confirmText || confirm(confirmText)) { - $this.prop("disabled"); - var loadingDelay = setTimeout(function() { - $this.button("loading"); - }, - 100); - - $.post($this.data("url"), - { 'messages[]': jobs }, - function() { - clearTimeout(loadingDelay); - window.location.reload(); - }); - } - - e.preventDefault(); - }); - - updateListState(); - }); - }; - - return Page; - })(); -})(window.Cap = window.Cap || {}); - -$(function() { - Cap.page = new Cap.Page(Cap.config); -}); - -(function() { - - var json = null; - - $(".openModal").click(function() { - const url = $(this).data("url"); - $.ajax({ - url: url, - dataType: "json", - success: function(data) { - json = data; - $("#formatBtn").click(); - $(".modal").modal("show"); - } - }); - }); - - $("#formatBtn").click(function() { - $("#jsonContent").JSONView(json); - }); - - $("#rawBtn").click(function() { - $("#jsonContent").text(JSON.stringify(json)); - }); - - $("#expandBtn").click(function() { - $("#jsonContent").JSONView("expand"); - }); - - $("#collapseBtn").click(function() { - $("#jsonContent").JSONView("collapse"); - }); - -})(); - -function nodeSwitch(id) { - console.log(`id:${id}`); - document.cookie = `cap.node=${escape(id)};`; - window.location.reload(); -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/Content/js/d3.layout.min.js b/src/DotNetCore.CAP.Dashboard/Content/js/d3.layout.min.js deleted file mode 100644 index 6704ca9..0000000 --- a/src/DotNetCore.CAP.Dashboard/Content/js/d3.layout.min.js +++ /dev/null @@ -1 +0,0 @@ -(function(){function a(a){var b=a.source,d=a.target,e=c(b,d),f=[b];while(b!==e)b=b.parent,f.push(b);var g=f.length;while(d!==e)f.splice(g,0,d),d=d.parent;return f}function b(a){var b=[],c=a.parent;while(c!=null)b.push(a),a=c,c=c.parent;return b.push(a),b}function c(a,c){if(a===c)return a;var d=b(a),e=b(c),f=d.pop(),g=e.pop(),h=null;while(f===g)h=f,f=d.pop(),g=e.pop();return h}function g(a){a.fixed|=2}function h(a){a!==f&&(a.fixed&=1)}function i(){j(),f.fixed&=1,e=f=null}function j(){f.px+=d3.event.dx,f.py+=d3.event.dy,e.resume()}function k(a,b,c){var d=0,e=0;a.charge=0;if(!a.leaf){var f=a.nodes,g=f.length,h=-1,i;while(++hd&&(c=b,d=e);return c}function u(a){return a.reduce(v,0)}function v(a,b){return a+b[1]}function w(a,b){return x(a,Math.ceil(Math.log(b.length)/Math.LN2+1))}function x(a,b){var c=-1,d=+a[0],e=(a[1]-d)/b,f=[];while(++c<=b)f[c]=e*c+d;return f}function y(a){return[d3.min(a),d3.max(a)]}function z(a,b){return a.sort=d3.rebind(a,b.sort),a.children=d3.rebind(a,b.children),a.links=D,a.value=d3.rebind(a,b.value),a.nodes=function(b){return E=!0,(a.nodes=a)(b)},a}function A(a){return a.children}function B(a){return a.value}function C(a,b){return b.value-a.value}function D(a){return d3.merge(a.map(function(a){return(a.children||[]).map(function(b){return{source:a,target:b}})}))}function F(a,b){return a.value-b.value}function G(a,b){var c=a._pack_next;a._pack_next=b,b._pack_prev=a,b._pack_next=c,c._pack_prev=b}function H(a,b){a._pack_next=b,b._pack_prev=a}function I(a,b){var c=b.x-a.x,d=b.y-a.y,e=a.r+b.r;return e*e-c*c-d*d>.001}function J(a){function l(a){b=Math.min(a.x-a.r,b),c=Math.max(a.x+a.r,c),d=Math.min(a.y-a.r,d),e=Math.max(a.y+a.r,e)}var b=Infinity,c=-Infinity,d=Infinity,e=-Infinity,f=a.length,g,h,i,j,k;a.forEach(K),g=a[0],g.x=-g.r,g.y=0,l(g);if(f>1){h=a[1],h.x=h.r,h.y=0,l(h);if(f>2){i=a[2],O(g,h,i),l(i),G(g,i),g._pack_prev=i,G(i,h),h=g._pack_next;for(var m=3;m0?(H(g,j),h=j,m--):(H(j,h),g=j,m--)}}}var q=(b+c)/2,r=(d+e)/2,s=0;for(var m=0;m0&&(a=d)}return a}function X(a,b){return a.x-b.x}function Y(a,b){return b.x-a.x}function Z(a,b){return a.depth-b.depth}function $(a,b){function c(a,d){var e=a.children;if(e&&(i=e.length)){var f,g=null,h=-1,i;while(++h=0)f=d[e]._tree,f.prelim+=b,f.mod+=b,b+=f.shift+(c+=f.change)}function ba(a,b,c){a=a._tree,b=b._tree;var d=c/(b.number-a.number);a.change+=d,b.change-=d,b.shift+=c,b.prelim+=c,b.mod+=c}function bb(a,b,c){return a._tree.ancestor.parent==b.parent?a._tree.ancestor:c}function bc(a){return{x:a.x,y:a.y,dx:a.dx,dy:a.dy}}function bd(a,b){var c=a.x+b[3],d=a.y+b[0],e=a.dx-b[1]-b[3],f=a.dy-b[0]-b[2];return e<0&&(c+=e/2,e=0),f<0&&(d+=f/2,f=0),{x:c,y:d,dx:e,dy:f}}d3.layout={},d3.layout.bundle=function(){return function(b){var c=[],d=-1,e=b.length;while(++de&&(e=h),d.push(h)}for(g=0;g=i[0]&&o<=i[1]&&(k=g[d3.bisect(j,o,1,m)-1],k.y+=n,k.push(e[f]));return g}var a=!0,b=Number,c=y,d=w;return e.value=function(a){return arguments.length?(b=a,e):b},e.range=function(a){return arguments.length?(c=d3.functor(a),e):c},e.bins=function(a){return arguments.length?(d=typeof a=="number"?function(b){return x(b,a)}:d3.functor(a),e):d},e.frequency=function(b){return arguments.length?(a=!!b,e):a},e},d3.layout.hierarchy=function(){function e(f,h,i){var j=b.call(g,f,h),k=E?f:{data:f};k.depth=h,i.push(k);if(j&&(m=j.length)){var l=-1,m,n=k.children=[],o=0,p=h+1;while(++l0&&(ba(bb(g,a,d),a,m),i+=m,j+=m),k+=g._tree.mod,i+=e._tree.mod,l+=h._tree.mod,j+=f._tree.mod;g&&!V(f)&&(f._tree.thread=g,f._tree.mod+=k-j),e&&!U(h)&&(h._tree.thread=e,h._tree.mod+=i-l,d=a)}return d}var f=a.call(this,d,e),g=f[0];$(g,function(a,b){a._tree={ancestor:a,prelim:0,mod:0,change:0,shift:0,number:b?b._tree.number+1:0}}),h(g),i(g,-g._tree.prelim);var k=W(g,Y),l=W(g,X),m=W(g,Z),n=k.x-b(k,l)/2,o=l.x+b(l,k)/2,p=m.depth||1;return $(g,function(a){a.x=(a.x-n)/(o-n)*c[0],a.y=a.depth/p*c[1],delete a._tree}),f}var a=d3.layout.hierarchy().sort(null).value(null),b=T,c=[1,1];return d.separation=function(a){return arguments.length?(b=a,d):b},d.size=function(a){return arguments.length?(c=a,d):c},z(d,a)},d3.layout.treemap=function(){function i(a,b){var c=-1,d=a.length,e,f;while(++c0)d.push(g=f[o-1]),d.area+=g.area,(k=l(d,n))<=h?(f.pop(),h=k):(d.area-=d.pop().area,m(d,n,c,!1),n=Math.min(c.dx,c.dy),d.length=d.area=0,h=Infinity);d.length&&(m(d,n,c,!0),d.length=d.area=0),b.forEach(j)}}function k(a){var b=a.children;if(b&&b.length){var c=e(a),d=b.slice(),f,g=[];i(d,c.dx*c.dy/a.value),g.area=0;while(f=d.pop())g.push(f),g.area+=f.area,f.z!=null&&(m(g,f.z?c.dx:c.dy,c,!d.length),g.length=g.area=0);b.forEach(k)}}function l(a,b){var c=a.area,d,e=0,f=Infinity,g=-1,i=a.length;while(++ge&&(e=d)}return c*=c,b*=b,c?Math.max(b*e*h/c,c/(b*f*h)):Infinity}function m(a,c,d,e){var f=-1,g=a.length,h=d.x,i=d.y,j=c?b(a.area/c):0,k;if(c==d.dx){if(e||j>d.dy)j=j?d.dy:0;while(++fd.dx)j=j?d.dx:0;while(++f=0?a.substring(b):(b=a.length,""),d=[];while(b>0)d.push(a.substring(b-=3,b+3));return d.reverse().join(",")+c}function w(a,b){return{scale:Math.pow(10,(8-b)*3),symbol:a}}function B(a){return function(b){return b<=0?0:b>=1?1:a(b)}}function C(a){return function(b){return 1-a(1-b)}}function D(a){return function(b){return.5*(b<.5?a(2*b):2-a(2-2*b))}}function E(a){return a}function F(a){return function(b){return Math.pow(b,a)}}function G(a){return 1-Math.cos(a*Math.PI/2)}function H(a){return Math.pow(2,10*(a-1))}function I(a){return 1-Math.sqrt(1-a*a)}function J(a,b){var c;return arguments.length<2&&(b=.45),arguments.length<1?(a=1,c=b/4):c=b/(2*Math.PI)*Math.asin(1/a),function(d){return 1+a*Math.pow(2,10*-d)*Math.sin((d-c)*2*Math.PI/b)}}function K(a){return a||(a=1.70158),function(b){return b*b*((a+1)*b-a)}}function L(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375}function M(){d3.event.stopPropagation(),d3.event.preventDefault()}function O(a){return a=="transform"?d3.interpolateTransform:d3.interpolate}function P(a,b){return b=b-(a=+a)?1/(b-a):0,function(c){return(c-a)*b}}function Q(a,b){return b=b-(a=+a)?1/(b-a):0,function(c){return Math.max(0,Math.min(1,(c-a)*b))}}function R(a,b,c){return new S(a,b,c)}function S(a,b,c){this.r=a,this.g=b,this.b=c}function T(a){return a<16?"0"+Math.max(0,a).toString(16):Math.min(255,a).toString(16)}function U(a,b,c){var d=0,e=0,f=0,g,h,i;g=/([a-z]+)\((.*)\)/i.exec(a);if(g){h=g[2].split(",");switch(g[1]){case"hsl":return c(parseFloat(h[0]),parseFloat(h[1])/100,parseFloat(h[2])/100);case"rgb":return b(W(h[0]),W(h[1]),W(h[2]))}}return(i=X[a])?b(i.r,i.g,i.b):(a!=null&&a.charAt(0)==="#"&&(a.length===4?(d=a.charAt(1),d+=d,e=a.charAt(2),e+=e,f=a.charAt(3),f+=f):a.length===7&&(d=a.substring(1,3),e=a.substring(3,5),f=a.substring(5,7)),d=parseInt(d,16),e=parseInt(e,16),f=parseInt(f,16)),b(d,e,f))}function V(a,b,c){var d=Math.min(a/=255,b/=255,c/=255),e=Math.max(a,b,c),f=e-d,g,h,i=(e+d)/2;return f?(h=i<.5?f/(e+d):f/(2-e-d),a==e?g=(b-c)/f+(b360?a-=360:a<0&&(a+=360),a<60?d+(e-d)*a/60:a<180?e:a<240?d+(e-d)*(240-a)/60:d}function g(a){return Math.round(f(a)*255)}var d,e;return a%=360,a<0&&(a+=360),b=b<0?0:b>1?1:b,c=c<0?0:c>1?1:c,e=c<=.5?c*(1+b):c+b-c*b,d=2*c-e,R(g(a+120),g(a),g(a-120))}function ba(a){return h(a,bd),a}function be(a){return function(){return bb(a,this)}}function bf(a){return function(){return bc(a,this)}}function bh(a,b){function f(){if(b=this.classList)return b.add(a);var b=this.className,d=b.baseVal!=null,e=d?b.baseVal:b;c.lastIndex=0,c.test(e)||(e=m(e+" "+a),d?b.baseVal=e:this.className=e)}function g(){if(b=this.classList)return b.remove(a);var b=this.className,d=b.baseVal!=null,e=d?b.baseVal:b;e=m(e.replace(c," ")),d?b.baseVal=e:this.className=e}function h(){(b.apply(this,arguments)?f:g).call(this)}var c=new RegExp("(^|\\s+)"+d3.requote(a)+"(\\s+|$)","g");if(arguments.length<2){var d=this.node();if(e=d.classList)return e.contains(a);var e=d.className;return c.lastIndex=0,c.test(e.baseVal!=null?e.baseVal:e)}return this.each(typeof b=="function"?h:b?f:g)}function bi(a){return{__data__:a}}function bj(a){return arguments.length||(a=d3.ascending),function(b,c){return a(b&&b.__data__,c&&c.__data__)}}function bl(a){return h(a,bm),a}function bn(a,b,c){h(a,br);var d={},e=d3.dispatch("start","end"),f=bu;return a.id=b,a.time=c,a.tween=function(b,c){return arguments.length<2?d[b]:(c==null?delete d[b]:d[b]=c,a)},a.ease=function(b){return arguments.length?(f=typeof b=="function"?b:d3.ease.apply(d3,arguments),a):f},a.each=function(b,c){return arguments.length<2?bv.call(a,b):(e.on(b,c),a)},d3.timer(function(g){return a.each(function(h,i,j){function p(a){if(o.active>b)return r();o.active=b;for(var f in d)(f=d[f].call(l,h,i))&&k.push(f);return e.start.call(l,h,i),q(a)||d3.timer(q,0,c),1}function q(a){if(o.active!==b)return r();var c=(a-m)/n,d=f(c),g=k.length;while(g>0)k[--g].call(l,d);if(c>=1)return r(),bt=b,e.end.call(l,h,i),bt=0,1}function r(){return--o.count||delete l.__transition__,1}var k=[],l=this,m=a[j][i].delay,n=a[j][i].duration,o=l.__transition__||(l.__transition__={active:0,count:0});++o.count,m<=g?p(g):d3.timer(p,m,c)}),1},0,c),a}function bp(a,b,c){return c!=""&&bo}function bq(a,b){function d(a,d,e){var f=b.call(this,a,d);return f==null?e!=""&&bo:e!=f&&c(e,f)}function e(a,d,e){return e!=b&&c(e,b)}var c=O(a);return typeof b=="function"?d:b==null?bp:(b+="",e)}function bv(a){for(var b=0,c=this.length;b=c.delay&&(c.flush=c.callback(a)),c=c.next;var d=bA()-b;d>24?(isFinite(d)&&(clearTimeout(by),by=setTimeout(bz,d)),bx=0):(bx=1,bB(bz))}function bA(){var a=null,b=bw,c=Infinity;while(b)b.flush?b=a?a.next=b.next:bw=b.next:(c=Math.min(c,b.then+b.delay),b=(a=b).next);return c}function bC(a){var b=[a.a,a.b],c=[a.c,a.d],d=bE(b),e=bD(b,c),f=bE(bF(c,b,-e));this.translate=[a.e,a.f],this.rotate=Math.atan2(a.b,a.a)*bH,this.scale=[d,f||0],this.skew=f?e/f*bH:0}function bD(a,b){return a[0]*b[0]+a[1]*b[1]}function bE(a){var b=Math.sqrt(bD(a,a));return a[0]/=b,a[1]/=b,b}function bF(a,b,c){return a[0]+=c*b[0],a[1]+=c*b[1],a}function bI(){}function bJ(a){var b=a[0],c=a[a.length-1];return b0;j--)e.push(c(f)*j)}else{for(;fi;g--);e=e.slice(f,g)}return e},d.tickFormat=function(a,e){arguments.length<2&&(e=bV);if(arguments.length<1)return e;var f=a/d.ticks().length,g=b===bX?(h=-1e-15,Math.floor):(h=1e-15,Math.ceil),h;return function(a){return a/c(g(b(a)+h))1){h=b[1],f=a[i],i++,d+="C"+(e[0]+g[0])+","+(e[1]+g[1])+","+(f[0]-h[0])+","+(f[1]-h[1])+","+f[0]+","+f[1];for(var j=2;j9&&(f=c*3/Math.sqrt(f),g[h]=f*d,g[h+1]=f*e));h=-1;while(++h<=i)f=(a[Math.min(i,h+1)][0]-a[Math.max(0,h-1)][0])/(6*(1+g[h]*g[h])),b.push([f||0,g[h]*f||0]);return b}function cK(a){return a.length<3?cq(a):a[0]+cw(a,cJ(a))}function cL(a){var b,c=-1,d=a.length,e,f;while(++c1){var d=bJ(a.domain()),e,f=-1,g=b.length,h=(b[1]-b[0])/++c,i,j;while(++f0;)(j=+b[f]-i*h)>=d[0]&&e.push(j);for(--f,i=0;++ib?1:a>=b?0:NaN},d3.descending=function(a,b){return ba?1:b>=a?0:NaN},d3.mean=function(a,b){var c=a.length,d,e=0,f=-1,g=0;if(arguments.length===1)while(++f1&&(a=a.map(b)),a=a.filter(j),a.length?d3.quantile(a.sort(d3.ascending),.5):undefined},d3.min=function(a,b){var c=-1,d=a.length,e,f;if(arguments.length===1){while(++cf&&(e=f)}else{while(++cf&&(e=f)}return e},d3.max=function(a,b){var c=-1,d=a.length,e,f;if(arguments.length===1){while(++ce&&(e=f)}else{while(++ce&&(e=f)}return e},d3.extent=function(a,b){var c=-1,d=a.length,e,f,g;if(arguments.length===1){while(++cf&&(e=f),gf&&(e=f),g1);return a+b*c*Math.sqrt(-2*Math.log(e)/e)}}},d3.sum=function(a,b){var c=0,d=a.length,e,f=-1;if(arguments.length===1)while(++f>1;a[e]>1;b0&&(e=f);return e},d3.last=function(a,b){var c=0,d=a.length,e=a[0],f;arguments.length===1&&(b=d3.ascending);while(++c=b.length)return e?e.call(a,c):d?c.sort(d):c;var h=-1,i=c.length,j=b[g++],k,l,m={};while(++h=b.length)return a;var e=[],f=c[d++],h;for(h in a)e.push({key:h,values:g(a[h],d)});return f&&e.sort(function(a,b){return f(a.key,b.key)}),e}var a={},b=[],c=[],d,e;return a.map=function(a){return f(a,0)},a.entries=function(a){return g(f(a,0),0)},a.key=function(c){return b.push(c),a},a.sortKeys=function(d){return c[b.length-1]=d,a},a.sortValues=function(b){return d=b,a},a.rollup=function(b){return e=b,a},a},d3.keys=function(a){var b=[];for(var c in a)b.push(c);return b},d3.values=function(a){var b=[];for(var c in a)b.push(a[c]);return b},d3.entries=function(a){var b=[];for(var c in a)b.push({key:c,value:a[c]});return b},d3.permute=function(a,b){var c=[],d=-1,e=b.length;while(++db)d.push(f);else while((f=a+c*++e)0&&(d=a.substring(c+1),a=a.substring(0,c)),this[a].on(d,b)},d3.format=function(a){var b=q.exec(a),c=b[1]||" ",d=b[3]||"",e=b[5],f=+b[6],g=b[7],h=b[8],i=b[9],j=1,k="",l=!1;h&&(h=+h.substring(1)),e&&(c="0",g&&(f-=Math.floor((f-1)/4)));switch(i){case"n":g=!0,i="g";break;case"%":j=100,k="%",i="f";break;case"p":j=100,k="%",i="r";break;case"d":l=!0,h=0;break;case"s":j=-1,i="r"}return i=="r"&&!h&&(i="g"),i=r[i]||t,function(a){if(l&&a%1)return"";var b=a<0&&(a=-a)?"−":d;if(j<0){var m=d3.formatPrefix(a,h);a*=m.scale,k=m.symbol}else a*=j;a=i(a,h);if(e){var n=a.length+b.length;n=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/,r={g:function(a,b){return a.toPrecision(b)},e:function(a,b){return a.toExponential(b)},f:function(a,b){return a.toFixed(b)},r:function(a,b){return d3.round(a,b=s(a,b)).toFixed(Math.max(0,Math.min(20,b)))}},v=["y","z","a","f","p","n","μ","m","","k","M","G","T","P","E","Z","Y"].map(w);d3.formatPrefix=function(a,b){var c=0;return a&&(a<0&&(a*=-1),b&&(a=d3.round(a,s(a,b))),c=1+Math.floor(1e-12+Math.log(a)/Math.LN10),c=Math.max(-24,Math.min(24,Math.floor((c<=0?c+1:c-1)/3)*3))),v[8+c/3]};var x=F(2),y=F(3),z={linear:function(){return E},poly:F,quad:function(){return x},cubic:function(){return y},sin:function(){return G},exp:function(){return H},circle:function(){return I},elastic:J,back:K,bounce:function(){return L}},A={"in":function(a){return a},out:C,"in-out":D,"out-in":function(a){return D(C(a))}};d3.ease=function(a){var b=a.indexOf("-"),c=b>=0?a.substring(0,b):a,d=b>=0?a.substring(b+1):"in";return B(A[d](z[c].apply(null,Array.prototype.slice.call(arguments,1))))},d3.event=null,d3.interpolate=function(a,b){var c=d3.interpolators.length,d;while(--c>=0&&!(d=d3.interpolators[c](a,b)));return d},d3.interpolateNumber=function(a,b){return b-=a,function(c){return a+b*c}},d3.interpolateRound=function(a,b){return b-=a,function(c){return Math.round(a+b*c)}},d3.interpolateString=function(a,b){var c,d,e,f=0,g=0,h=[],i=[],j,k;N.lastIndex=0;for(d=0;c=N.exec(b);++d)c.index&&h.push(b.substring(f,g=c.index)),i.push({i:h.length,x:c[0]}),h.push(null),f=N.lastIndex;f1){while(++e0&&(a=a.substring(0,e)),arguments.length<2?(e=this.node()[d])&&e._:this.each(function(e,f){function h(a){var c=d3.event;d3.event=a;try{b.call(g,g.__data__,f)}finally{d3.event=c}}var g=this;g[d]&&g.removeEventListener(a,g[d],c),b&&g.addEventListener(a,g[d]=h,c),h._=b})},bd.each=function(a){for(var b=-1,c=this.length;++b=cg?e?"M0,"+f+"A"+f+","+f+" 0 1,1 0,"+ -f+"A"+f+","+f+" 0 1,1 0,"+f+"M0,"+e+"A"+e+","+e+" 0 1,0 0,"+ -e+"A"+e+","+e+" 0 1,0 0,"+e+"Z":"M0,"+f+"A"+f+","+f+" 0 1,1 0,"+ -f+"A"+f+","+f+" 0 1,1 0,"+f+"Z":e?"M"+f*k+","+f*l+"A"+f+","+f+" 0 "+j+",1 "+f*m+","+f*n+"L"+e*m+","+e*n+"A"+e+","+e+" 0 "+j+",0 "+e*k+","+e*l+"Z":"M"+f*k+","+f*l+"A"+f+","+f+" 0 "+j+",1 "+f*m+","+f*n+"L0,0"+"Z"}var a=ch,b=ci,c=cj,d=ck;return e.innerRadius=function(b){return arguments.length?(a=d3.functor(b),e):a},e.outerRadius=function(a){return arguments.length?(b=d3.functor(a),e):b},e.startAngle=function(a){return arguments.length?(c=d3.functor(a),e):c},e.endAngle=function(a){return arguments.length?(d=d3.functor(a),e):d},e.centroid=function(){var e=(a.apply(this,arguments)+b.apply(this,arguments))/2,f=(c.apply(this,arguments)+d.apply(this,arguments))/2+cf;return[Math.cos(f)*e,Math.sin(f)*e]},e};var cf=-Math.PI/2,cg=2*Math.PI-1e-6;d3.svg.line=function(){return cl(Object)};var cp={linear:cq,"step-before":cr,"step-after":cs,basis:cy,"basis-open":cz,"basis-closed":cA,bundle:cB,cardinal:cv,"cardinal-open":ct,"cardinal-closed":cu,monotone:cK},cD=[0,2/3,1/3,0],cE=[0,1/3,2/3,0],cF=[0,1/6,2/3,1/6];d3.svg.line.radial=function(){var a=cl(cL);return a.radius=a.x,delete a.x,a.angle=a.y,delete a.y,a},cr.reverse=cs,cs.reverse=cr,d3.svg.area=function(){return cM(Object)},d3.svg.area.radial=function(){var a=cM(cL);return a.radius=a.x,delete a.x,a.innerRadius=a.x0,delete a.x0,a.outerRadius=a.x1,delete a.x1,a.angle=a.y,delete a.y,a.startAngle=a.y0,delete a.y0,a.endAngle=a.y1,delete a.y1,a},d3.svg.chord=function(){function f(c,d){var e=g(this,a,c,d),f=g(this,b,c,d);return"M"+e.p0+i(e.r,e.p1)+(h(e,f)?j(e.r,e.p1,e.r,e.p0):j(e.r,e.p1,f.r,f.p0)+i(f.r,f.p1)+j(f.r,f.p1,e.r,e.p0))+"Z"}function g(a,b,f,g){var h=b.call(a,f,g),i=c.call(a,h,g),j=d.call(a,h,g)+cf,k=e.call(a,h,g)+cf;return{r:i,a0:j,a1:k,p0:[i*Math.cos(j),i*Math.sin(j)],p1:[i*Math.cos(k),i*Math.sin(k)]}}function h(a,b){return a.a0==b.a0&&a.a1==b.a1}function i(a,b){return"A"+a+","+a+" 0 0,1 "+b}function j(a,b,c,d){return"Q 0,0 "+d}var a=cP,b=cQ,c=cR,d=cj,e=ck;return f.radius=function(a){return arguments.length?(c=d3.functor(a),f):c},f.source=function(b){return arguments.length?(a=d3.functor(b),f):a},f.target=function(a){return arguments.length?(b=d3.functor(a),f):b},f.startAngle=function(a){return arguments.length?(d=d3.functor(a),f):d},f.endAngle=function(a){return arguments.length?(e=d3.functor(a),f):e},f},d3.svg.diagonal=function(){function d(d,e){var f=a.call(this,d,e),g=b.call(this,d,e),h=(f.y+g.y)/2,i=[f,{x:f.x,y:h},{x:g.x,y:h},g];return i=i.map(c),"M"+i[0]+"C"+i[1]+" "+i[2]+" "+i[3]}var a=cP,b=cQ,c=cU;return d.source=function(b){return arguments.length?(a=d3.functor(b),d):a},d.target=function(a){return arguments.length?(b=d3.functor(a),d):b},d.projection=function(a){return arguments.length?(c=a,d):c},d},d3.svg.diagonal.radial=function(){var a=d3.svg.diagonal(),b=cU,c=a.projection;return a.projection=function(a){return arguments.length?c(cV(b=a)):b},a},d3.svg.mouse=function(a){return cX(a,d3.event)};var cW=/WebKit/.test(navigator.userAgent)?-1:0;d3.svg.touches=function(a,b){return arguments.length<2&&(b=d3.event.touches),b?d(b).map(function(b){var c=cX(a,b);return c.identifier=b.identifier,c}):[]},d3.svg.symbol=function(){function c(c,d){return(c$[a.call(this,c,d)]||c$.circle)(b.call(this,c,d))}var a=cZ,b=cY;return c.type=function(b){return arguments.length?(a=d3.functor(b),c):a},c.size=function(a){return arguments.length?(b=d3.functor(a),c):b},c};var c$={circle:function(a){var b=Math.sqrt(a/Math.PI);return"M0,"+b+"A"+b+","+b+" 0 1,1 0,"+ -b+"A"+b+","+b+" 0 1,1 0,"+b+"Z"},cross:function(a){var b=Math.sqrt(a/5)/2;return"M"+ -3*b+","+ -b+"H"+ -b+"V"+ -3*b+"H"+b+"V"+ -b+"H"+3*b+"V"+b+"H"+b+"V"+3*b+"H"+ -b+"V"+b+"H"+ -3*b+"Z"},diamond:function(a){var b=Math.sqrt(a/(2*da)),c=b*da;return"M0,"+ -b+"L"+c+",0"+" 0,"+b+" "+ -c+",0"+"Z"},square:function(a){var b=Math.sqrt(a)/2;return"M"+ -b+","+ -b+"L"+b+","+ -b+" "+b+","+b+" "+ -b+","+b+"Z"},"triangle-down":function(a){var b=Math.sqrt(a/c_),c=b*c_/2;return"M0,"+c+"L"+b+","+ -c+" "+ -b+","+ -c+"Z"},"triangle-up":function(a){var b=Math.sqrt(a/c_),c=b*c_/2;return"M0,"+ -c+"L"+b+","+c+" "+ -b+","+c+"Z"}};d3.svg.symbolTypes=d3.keys(c$);var c_=Math.sqrt(3),da=Math.tan(30*Math.PI/180);d3.svg.axis=function(){function j(j){j.each(function(k,l,m){var n=d3.select(this),o=j.delay?function(a){var b=bt;try{return bt=j.id,a.transition().delay(j[m][l].delay).duration(j[m][l].duration).ease(j.ease())}finally{bt=b}}:Object,p=a.ticks.apply(a,g),q=h==null?a.tickFormat.apply(a,g):h,r=dd(a,p,i),s=n.selectAll(".minor").data(r,String),t=s.enter().insert("svg:line","g").attr("class","tick minor").style("opacity",1e-6),u=o(s.exit()).style("opacity",1e-6).remove(),v=o(s).style("opacity",1),w=n.selectAll("g").data(p,String),x=w.enter().insert("svg:g","path").style("opacity",1e-6),y=o(w.exit()).style("opacity",1e-6).remove(),z=o(w).style("opacity",1),A,B=bJ(a.range()),C=n.selectAll(".domain").data([0]),D=C.enter().append("svg:path").attr("class","domain"),E=o(C),F=this.__chart__||a;this.__chart__=a.copy(),x.append("svg:line").attr("class","tick"),x.append("svg:text"),z.select("text").text(q);switch(b){case"bottom":A=db,v.attr("x2",0).attr("y2",d),z.select("line").attr("x2",0).attr("y2",c),z.select("text").attr("x",0).attr("y",Math.max(c,0)+f).attr("dy",".71em").attr("text-anchor","middle"),E.attr("d","M"+B[0]+","+e+"V0H"+B[1]+"V"+e);break;case"top":A=db,v.attr("x2",0).attr("y2",-d),z.select("line").attr("x2",0).attr("y2",-c),z.select("text").attr("x",0).attr("y",-(Math.max(c,0)+f)).attr("dy","0em").attr("text-anchor","middle"),E.attr("d","M"+B[0]+","+ -e+"V0H"+B[1]+"V"+ -e);break;case"left":A=dc,v.attr("x2",-d).attr("y2",0),z.select("line").attr("x2",-c).attr("y2",0),z.select("text").attr("x",-(Math.max(c,0)+f)).attr("y",0).attr("dy",".32em").attr("text-anchor","end"),E.attr("d","M"+ -e+","+B[0]+"H0V"+B[1]+"H"+ -e);break;case"right":A=dc,v.attr("x2",d).attr("y2",0),z.select("line").attr("x2",c).attr("y2",0),z.select("text").attr("x",Math.max(c,0)+f).attr("y",0).attr("dy",".32em").attr("text-anchor","start"),E.attr("d","M"+e+","+B[0]+"H0V"+B[1]+"H"+e)}x.call(A,F),z.call(A,a),y.call(A,a),t.call(A,F),v.call(A,a),u.call(A,a)})}var a=d3.scale.linear(),b="bottom",c=6,d=6,e=6,f=3,g=[10],h,i=0;return j.scale=function(b){return arguments.length?(a=b,j):a},j.orient=function(a){return arguments.length?(b=a,j):b},j.ticks=function(){return arguments.length?(g=arguments,j):g},j.tickFormat=function(a){return arguments.length?(h=a,j):h},j.tickSize=function(a,b,f){if(!arguments.length)return c;var g=arguments.length-1;return c=+a,d=g>1?+b:c,e=g>0?+arguments[g]:c,j},j.tickPadding=function(a){return arguments.length?(f=+a,j):f},j.tickSubdivide=function(a){return arguments.length?(i=+a,j):i},j},d3.svg.brush=function(){function e(a){var g=b&&c?["n","e","s","w","nw","ne","se","sw"]:b?["e","w"]:c?["n","s"]:[];a.each(function(){var a=d3.select(this).on("mousedown.brush",f),h=a.selectAll(".background").data([,]),i=a.selectAll(".extent").data([,]),j=a.selectAll(".resize").data(g,String),k;h.enter().append("svg:rect").attr("class","background").style("visibility","hidden").style("pointer-events","all").style("cursor","crosshair"),i.enter().append("svg:rect").attr("class","extent").style("cursor","move"),j.enter().append("svg:rect").attr("class",function(a){return"resize "+a}).attr("width",6).attr("height",6).style("visibility","hidden").style("pointer-events",e.empty()?"none":"all").style("cursor",function(a){return dw[a]}),j.exit().remove(),b&&(k=bJ(b.range()),h.attr("x",k[0]).attr("width",k[1]-k[0]),dp(a,d)),c&&(k=bJ(c.range()),h.attr("y",k[0]).attr("height",k[1]-k[0]),dq(a,d))})}function f(){var a=d3.select(d3.event.target);de=e,dg=this,dj=d,dn=d3.svg.mouse(dg),(dk=a.classed("extent"))?(dn[0]=d[0][0]-dn[0],dn[1]=d[0][1]-dn[1]):a.classed("resize")?(dl=d3.event.target.__data__,dn[0]=d[+/w$/.test(dl)][0],dn[1]=d[+/^n/.test(dl)][1]):d3.event.altKey&&(dm=dn.slice()),dh=!/^(n|s)$/.test(dl)&&b,di=!/^(e|w)$/.test(dl)&&c,df=g(this,arguments),df("brushstart"),dt(),M()}function g(b,c){return function(d){var f=d3.event;try{d3.event={type:d,target:e},a[d].apply(b,c)}finally{d3.event=f}}}var a=d3.dispatch("brushstart","brush","brushend"),b,c,d=[[0,0],[0,0]];return e.x=function(a){return arguments.length?(b=a,e):b},e.y=function(a){return arguments.length?(c=a,e):c},e.extent=function(a){var f,g,h,i,j;return arguments.length?(b&&(f=a[0],g=a[1],c&&(f=f[0],g=g[0]),f=b(f),g=b(g),ga?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b="length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){ -return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|&#?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*\s*$/g,ia={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n("