├── .dockerignore ├── .gitattributes ├── .gitignore ├── EasySlideVerification.sln ├── EasySlideVerification ├── App_Data │ └── Images │ │ └── Slide │ │ ├── bg-s.jpg │ │ └── slide-01.png ├── Common │ ├── ImageUtil.cs │ ├── SlideVerificationCreater.cs │ └── StringExtension.cs ├── EasySlideVerification.csproj ├── EasySlideVerification.xml ├── Extension │ ├── SlideVerificationExtension.cs │ ├── SlideVerificationOptions.cs │ └── SlideVerificationRedisOptions.cs ├── ISlideVerifyService.cs ├── ImageProvider │ ├── BackgroundLocalImageService.cs │ ├── IImageService.cs │ ├── LocalImageProviderBase.cs │ └── SlideLocalImageService.cs ├── Model │ ├── SlideVerificationInfo.cs │ ├── SlideVerificationParam.cs │ ├── SlideVerificationPlainInfo.cs │ └── VerifyParam.cs ├── SlideVerifyService.cs └── Store │ ├── ISlideVerificationStore.cs │ ├── VerificationInMemoryStore.cs │ └── VerificationInRedisStore.cs ├── EasySlideVerificationDemo ├── .env.development ├── .env.production ├── EasySlideVerificationDemo.njsproj ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ └── index.html └── src │ ├── App.vue │ ├── api │ └── VerificationDemoApi.js │ ├── components │ └── Home.vue │ ├── main.js │ ├── slider │ └── slider.vue │ └── utils │ └── request.js ├── EasySlideVerificationDemoServer ├── App_Data │ └── Images │ │ └── Slide │ │ ├── bg-s.jpg │ │ └── slide-01.png ├── Controllers │ └── HomeController.cs ├── Dockerfile ├── Dockerfile2 ├── EasySlideVerificationDemoServer.csproj ├── Models │ └── ErrorViewModel.cs ├── Program.cs ├── Properties │ ├── PublishProfiles │ │ ├── linux.pubxml │ │ └── windows.pubxml │ └── launchSettings.json ├── Startup.cs ├── Views │ ├── Home │ │ └── Index.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ └── _Layout.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml ├── appsettings.Development.json ├── appsettings.json └── wwwroot │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── js │ ├── site.js │ └── site.min.js │ ├── lib │ └── jquery │ │ ├── .bower.json │ │ ├── LICENSE.txt │ │ └── dist │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map │ └── slider │ ├── slider.css │ └── slider.js └── README.md /.dockerignore: -------------------------------------------------------------------------------- 1 | .dockerignore 2 | .env 3 | .git 4 | .gitignore 5 | .vs 6 | .vscode 7 | */bin 8 | */obj 9 | **/.toolstarget -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | /EasySlideVerificationDemo/dist 263 | -------------------------------------------------------------------------------- /EasySlideVerification.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.705 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasySlideVerification", "EasySlideVerification\EasySlideVerification.csproj", "{434B73A5-93A9-412A-8733-CA686933916A}" 7 | EndProject 8 | Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "EasySlideVerificationDemo", "EasySlideVerificationDemo\EasySlideVerificationDemo.njsproj", "{98BB4A9B-7DA8-4661-82CE-C279C53C0798}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasySlideVerificationDemoServer", "EasySlideVerificationDemoServer\EasySlideVerificationDemoServer.csproj", "{77D9E41A-D7A7-468D-AD50-FD5760F19A96}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {434B73A5-93A9-412A-8733-CA686933916A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {434B73A5-93A9-412A-8733-CA686933916A}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {434B73A5-93A9-412A-8733-CA686933916A}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {434B73A5-93A9-412A-8733-CA686933916A}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {98BB4A9B-7DA8-4661-82CE-C279C53C0798}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {98BB4A9B-7DA8-4661-82CE-C279C53C0798}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {98BB4A9B-7DA8-4661-82CE-C279C53C0798}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {98BB4A9B-7DA8-4661-82CE-C279C53C0798}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {77D9E41A-D7A7-468D-AD50-FD5760F19A96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {77D9E41A-D7A7-468D-AD50-FD5760F19A96}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {77D9E41A-D7A7-468D-AD50-FD5760F19A96}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {77D9E41A-D7A7-468D-AD50-FD5760F19A96}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {4375C6D0-04F1-4114-A0AC-2029702DBE34} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /EasySlideVerification/App_Data/Images/Slide/bg-s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonosun/EasySlideVerification/4a437ff92c3f4463cc697126127526bf3186b953/EasySlideVerification/App_Data/Images/Slide/bg-s.jpg -------------------------------------------------------------------------------- /EasySlideVerification/App_Data/Images/Slide/slide-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonosun/EasySlideVerification/4a437ff92c3f4463cc697126127526bf3186b953/EasySlideVerification/App_Data/Images/Slide/slide-01.png -------------------------------------------------------------------------------- /EasySlideVerification/Common/ImageUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Drawing.Imaging; 5 | using System.IO; 6 | using System.Text; 7 | 8 | namespace EasySlideVerification.Common 9 | { 10 | /// 11 | /// 12 | /// 13 | public class ImageUtil 14 | { 15 | /// 16 | /// byte数组转Image 17 | /// 18 | /// 19 | /// 20 | public static Bitmap GetImage(byte[] imageByteArr) 21 | { 22 | using (var stream = new MemoryStream(imageByteArr)) 23 | { 24 | return Bitmap.FromStream(stream) as Bitmap; 25 | } 26 | } 27 | 28 | /// 29 | /// Image转Byte数组 30 | /// 31 | /// 32 | /// 33 | public static byte[] ImageToByteArr(Image sourceImage, ImageFormat format) 34 | { 35 | using (MemoryStream stream = new MemoryStream()) 36 | { 37 | sourceImage.Save(stream, format); 38 | byte[] buffer = new byte[stream.Length]; 39 | stream.Position = 0; 40 | stream.Read(buffer, 0, buffer.Length); 41 | stream.Close(); 42 | 43 | return buffer; 44 | } 45 | } 46 | 47 | /// 48 | /// Image转base64 49 | /// 50 | /// 51 | /// 52 | public static string ImageToBase64(Image sourceImage, ImageFormat format) 53 | { 54 | byte[] buffer = ImageToByteArr(sourceImage, format); 55 | StringBuilder result = new StringBuilder(); 56 | result.Append($"data:image/{format};base64,"); 57 | result.Append(Convert.ToBase64String(buffer)); 58 | return result.ToString(); 59 | } 60 | 61 | /// 62 | /// Image转base64 63 | /// 64 | /// 65 | /// 66 | public static string ImageToBase64(byte[] buffer, ImageFormat format) 67 | { 68 | StringBuilder result = new StringBuilder(); 69 | result.Append($"data:image/{format};base64,"); 70 | result.Append(Convert.ToBase64String(buffer)); 71 | return result.ToString(); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /EasySlideVerification/Common/SlideVerificationCreater.cs: -------------------------------------------------------------------------------- 1 | using EasySlideVerification.Common; 2 | using EasySlideVerification.Model; 3 | using EasySlideVerification.Store; 4 | using System; 5 | using System.Drawing; 6 | using System.Drawing.Drawing2D; 7 | using System.Drawing.Imaging; 8 | using System.IO; 9 | using System.Net.Http; 10 | using System.Text; 11 | 12 | namespace EasySlideVerification.Common 13 | { 14 | /// 15 | /// 16 | /// 17 | public class SlideVerificationCreater 18 | { 19 | /// 20 | /// 21 | /// 22 | private SlideVerificationCreater() { } 23 | 24 | public static readonly SlideVerificationCreater Instance = new SlideVerificationCreater(); 25 | 26 | Random random = new Random(); 27 | 28 | /// 29 | /// 创建图片滑动数据 30 | /// 31 | public SlideVerificationInfo Create(SlideVerificationParam param) 32 | { 33 | SlideVerificationInfo result = new SlideVerificationInfo(); 34 | result.Key = Guid.NewGuid().ToString("N"); 35 | 36 | using (Bitmap coverImage = ImageUtil.GetImage(param.SlideImage)) 37 | using (Bitmap sourceImage = ImageUtil.GetImage(param.BackgroundImage)) 38 | { 39 | result.BgWidth = sourceImage.Width; 40 | result.BgHeight = sourceImage.Height; 41 | result.SlideWidth = coverImage.Width; 42 | result.SlideHeight = coverImage.Height; 43 | result.PositionX = random.Next(coverImage.Width, sourceImage.Width - coverImage.Width - param.Edge); 44 | result.PositionY = random.Next(coverImage.Height, sourceImage.Height - coverImage.Height); 45 | 46 | //滑块图片 47 | result.SlideImg = CaptureImage(sourceImage, coverImage, result.PositionX, result.PositionY); 48 | //背景图片 49 | result.BackgroundImg = DrawBackground(sourceImage, coverImage, result.PositionX, result.PositionY, param.MixedCount); 50 | } 51 | 52 | return result; 53 | } 54 | 55 | /// 56 | /// 画背景图 57 | /// 58 | /// 59 | /// 60 | /// 61 | /// 62 | /// 63 | /// 64 | private byte[] DrawBackground(Bitmap sourceImage, Bitmap coverImage, int positionX, int positionY, int mixedCount) 65 | { 66 | //背景图片 67 | using (Graphics graphics = Graphics.FromImage(sourceImage)) 68 | { 69 | graphics.DrawImage(coverImage, positionX, positionY, coverImage.Width, coverImage.Height); 70 | graphics.Save(); 71 | } 72 | 73 | //画混淆图 74 | DrawMix(sourceImage, coverImage, mixedCount, positionX, positionY); 75 | 76 | return ImageUtil.ImageToByteArr(sourceImage, ImageFormat.Jpeg); 77 | } 78 | 79 | /// 80 | /// 画混淆图 81 | /// 82 | /// 83 | /// 84 | /// 85 | /// 86 | /// 87 | private void DrawMix(Image sourceImage, Image mixImage, int count, int slidePositionX, int slidePositionY) 88 | { 89 | using (Graphics graphics = Graphics.FromImage(sourceImage)) 90 | { 91 | int coverWidth = mixImage.Width; 92 | int coverHeight = mixImage.Height; 93 | for (int i = 0; i < count; i++) 94 | { 95 | int offsetX = random.Next(mixImage.Width, sourceImage.Width - mixImage.Width); 96 | while (Math.Abs(offsetX - slidePositionX) < coverWidth * 2) 97 | { 98 | offsetX = random.Next(mixImage.Width, sourceImage.Width - mixImage.Width); 99 | } 100 | 101 | //int offsetY = random.Next(mixImage.Height, sourceImage.Height - mixImage.Height); 102 | int offsetY = slidePositionY; 103 | 104 | graphics.DrawImage(mixImage, offsetX, offsetY, coverWidth, coverHeight); 105 | graphics.Save(); 106 | } 107 | } 108 | } 109 | 110 | /// 111 | /// 从大图中截取一部分图片 112 | /// 113 | /// 来源图片 114 | /// 从偏移X坐标位置开始截取 115 | /// 从偏移Y坐标位置开始截取 116 | /// 保存图片的宽度 117 | /// 保存图片的高度 118 | /// 119 | private byte[] CaptureImage(Image fromImage, int offsetX, int offsetY, int width, int height) 120 | { 121 | //创建新图位图 122 | using (Bitmap bitmap = new Bitmap(width, height)) 123 | //创建作图区域 124 | using (Graphics graphic = Graphics.FromImage(bitmap)) 125 | { 126 | //截取原图相应区域写入作图区 127 | graphic.DrawImage(fromImage, 0, 0, new Rectangle(offsetX, offsetY, width + 6, height + 6), GraphicsUnit.Pixel); 128 | graphic.Save(); 129 | 130 | return ImageUtil.ImageToByteArr(bitmap, ImageFormat.Png); 131 | } 132 | } 133 | 134 | /// 135 | /// 从大图中根据像素点,生成图片,作为拼图块 136 | /// 137 | /// 138 | /// 139 | /// 140 | /// 141 | /// 142 | private byte[] CaptureImage(Bitmap backgroudImage, Bitmap coverImage, int offsetX, int offsetY) 143 | { 144 | //创建新图位图 145 | using (Bitmap bitmap = new Bitmap(coverImage.Width, coverImage.Height)) 146 | { 147 | for (int y = 0; y < coverImage.Height; y++) 148 | { 149 | for (int x = 0; x < coverImage.Width; x++) 150 | { 151 | var pointColor = coverImage.GetPixel(x, y); 152 | if (pointColor.A != 0) 153 | { 154 | bitmap.SetPixel(x, y, backgroudImage.GetPixel(offsetX + x, offsetY + y)); 155 | } 156 | } 157 | } 158 | 159 | return ImageUtil.ImageToByteArr(bitmap, ImageFormat.Png); 160 | } 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /EasySlideVerification/Common/StringExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace EasySlideVerification.Common 6 | { 7 | /// 8 | /// 字符串扩展方法 9 | /// 10 | internal static class StringExtension 11 | { 12 | /// 13 | /// 是否为空 14 | /// 15 | /// 16 | /// 17 | public static bool IsEmpty(this string val) 18 | { 19 | return string.IsNullOrEmpty(val); 20 | } 21 | 22 | /// 23 | /// 是否非空 24 | /// 25 | /// 26 | /// 27 | public static bool IsNotEmpty(this string val) 28 | { 29 | return !string.IsNullOrEmpty(val); 30 | } 31 | 32 | /// 33 | /// 转换为decimal类型 34 | /// 35 | /// 36 | /// 37 | public static decimal ToDecimal(this string val) 38 | { 39 | decimal result = 0; 40 | if (!string.IsNullOrEmpty(val)) 41 | { 42 | decimal.TryParse(val, out result); 43 | } 44 | 45 | return result; 46 | } 47 | 48 | /// 49 | /// 转换为int类型 50 | /// 51 | /// 52 | /// 53 | public static int ToInt(this string val) 54 | { 55 | int result = 0; 56 | if (!string.IsNullOrEmpty(val)) 57 | { 58 | int.TryParse(val, out result); 59 | } 60 | 61 | return result; 62 | } 63 | 64 | /// 65 | /// 转换为long类型 66 | /// 67 | /// 68 | /// 69 | public static long ToLong(this string val) 70 | { 71 | long result = 0; 72 | if (!string.IsNullOrEmpty(val)) 73 | { 74 | long.TryParse(val, out result); 75 | } 76 | 77 | return result; 78 | } 79 | 80 | /// 81 | /// 转换为DateTime类型 82 | /// 83 | /// 84 | /// 85 | public static DateTime ToDateTime(this string val) 86 | { 87 | DateTime result = DateTime.MinValue; 88 | if (!string.IsNullOrEmpty(val)) 89 | { 90 | DateTime.TryParse(val, out result); 91 | } 92 | 93 | return result; 94 | } 95 | 96 | /// 97 | /// 转换为Base64编码字符串 98 | /// 99 | /// 100 | /// 101 | public static string ToBase64Str(this string val) 102 | { 103 | byte[] urlByteArr = Encoding.UTF8.GetBytes(val); 104 | return Convert.ToBase64String(urlByteArr); 105 | } 106 | 107 | /// 108 | /// Base64解码字符串 109 | /// 110 | /// 111 | public static string FromBase64Str(this string val) 112 | { 113 | var urlByteArr = Convert.FromBase64String(val); 114 | return Encoding.UTF8.GetString(urlByteArr); 115 | } 116 | 117 | /// 118 | /// 字符串模板填充 119 | /// 120 | /// 121 | /// 122 | /// 123 | public static string FormatWith(this string format, object param) 124 | { 125 | return string.Format(format, param); 126 | } 127 | 128 | /// 129 | /// 字符串模板填充 130 | /// 131 | /// 132 | /// 133 | /// 134 | public static string FormatWith(this string format, params object[] param) 135 | { 136 | return string.Format(format, param); 137 | } 138 | 139 | /// 140 | /// 混淆替换字符串,如银行账户 141 | /// 142 | /// 143 | /// 144 | public static string Mix(this string val) 145 | { 146 | string mixedWord = "*"; 147 | string result = string.Empty; 148 | if (val.IsNotEmpty()) 149 | { 150 | if (val.Length == 1) 151 | { 152 | result = mixedWord; 153 | } 154 | else if (val.Length == 2) 155 | { 156 | char[] arr = val.ToArray(); 157 | result = arr[0] + mixedWord; 158 | } 159 | else if (val.Length <= 5) 160 | { 161 | result = MixWord(val, 1, mixedWord); 162 | } 163 | else if (val.Length <= 10) 164 | { 165 | result = MixWord(val, 2, mixedWord); 166 | } 167 | else 168 | { 169 | result = MixWord(val, 4, mixedWord); 170 | } 171 | } 172 | 173 | return result; 174 | } 175 | 176 | /// 177 | /// 178 | /// 179 | /// 待处理字符串 180 | /// 在字符串两端,可见的字符数. 181 | /// 用于隐藏替代的字符串.例如:* 182 | /// 183 | private static string MixWord(string val, int showCount, string mixedWord) 184 | { 185 | char[] arr = val.ToArray(); 186 | StringBuilder content = new StringBuilder(); 187 | for (int i = 0; i < arr.Length; i++) 188 | { 189 | if (i < showCount || i >= arr.Length - showCount) 190 | { 191 | content.Append(arr[i]); 192 | } 193 | else 194 | { 195 | //防止字符串过长 196 | if (content.Length < 12) 197 | { 198 | content.Append(mixedWord); 199 | } 200 | } 201 | } 202 | return content.ToString(); 203 | } 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /EasySlideVerification/EasySlideVerification.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1;netcoreapp2.2 5 | A Slide Verification Module 6 | 拼图校验模块,用于生成校验拼图及验证,支持redis,支持分布式部署。 7 | 8 | 1.0.0.2:修复window和linux目录分隔符不同的问题,以适应linux部署。 9 | true 10 | 11 | https://github.com/sonosun/EasySlideVerification 12 | 1.0.0.2 13 | 14 | 15 | 16 | D:\source\EasyDotNet\EasySlideVerification\EasySlideVerification\EasySlideVerification.xml 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | PreserveNewest 37 | 38 | 39 | PreserveNewest 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /EasySlideVerification/EasySlideVerification.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | EasySlideVerification 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | byte数组转Image 15 | 16 | 17 | 18 | 19 | 20 | 21 | Image转Byte数组 22 | 23 | 24 | 25 | 26 | 27 | 28 | Image转base64 29 | 30 | 31 | 32 | 33 | 34 | 35 | Image转base64 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 创建图片滑动数据 53 | 54 | 55 | 56 | 57 | 画背景图 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 画混淆图 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 从大图中截取一部分图片 79 | 80 | 来源图片 81 | 从偏移X坐标位置开始截取 82 | 从偏移Y坐标位置开始截取 83 | 保存图片的宽度 84 | 保存图片的高度 85 | 86 | 87 | 88 | 89 | 从大图中根据像素点,生成图片,作为拼图块 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 字符串扩展方法 100 | 101 | 102 | 103 | 104 | 是否为空 105 | 106 | 107 | 108 | 109 | 110 | 111 | 是否非空 112 | 113 | 114 | 115 | 116 | 117 | 118 | 转换为decimal类型 119 | 120 | 121 | 122 | 123 | 124 | 125 | 转换为int类型 126 | 127 | 128 | 129 | 130 | 131 | 132 | 转换为long类型 133 | 134 | 135 | 136 | 137 | 138 | 139 | 转换为DateTime类型 140 | 141 | 142 | 143 | 144 | 145 | 146 | 转换为Base64编码字符串 147 | 148 | 149 | 150 | 151 | 152 | 153 | Base64解码字符串 154 | 155 | 156 | 157 | 158 | 159 | 字符串模板填充 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 字符串模板填充 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 混淆替换字符串,如银行账户 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 待处理字符串 185 | 在字符串两端,可见的字符数. 186 | 用于隐藏替代的字符串.例如:* 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 注册校验模块 197 | 198 | 199 | 200 | 201 | 202 | 注册校验模块,使用Redis存储 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 可接受的误差范围 219 | 220 | 221 | 222 | 223 | 右边框距离(防止由于太靠近右侧,滑动按钮无法到达) 224 | 225 | 226 | 227 | 228 | 混淆点数量 229 | 230 | 231 | 232 | 233 | 过期时间 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | redis链接,如:127.0.0.1:6379 244 | 245 | 246 | 247 | 248 | redis库索引(默认为:0) 249 | 250 | 251 | 252 | 253 | redis缓存key前缀,起到对缓存项分组的作用。可以为空,但建议使用 slide: 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 随机数 295 | 296 | 297 | 298 | 299 | 图片列表 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 滑块校验 331 | 332 | 333 | 334 | 335 | 创建图片滑动数据(图片以byte数组格式返回) 336 | 337 | 338 | 339 | 340 | 验证结果 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 背景图片(byte数组图片数据) 358 | 359 | 360 | 361 | 362 | 滑块图片(byte数组图片数据) 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 背景图片原图 403 | 404 | 405 | 406 | 407 | 滑块图 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 背景图片 433 | 434 | 435 | 436 | 437 | 滑块图片 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 背景图片高度 453 | 454 | 455 | 456 | 457 | 背景图片宽度 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 对象拷贝 473 | 474 | 475 | 476 | 477 | 478 | 479 | 校验参数 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 是否校验成功后移除缓存 500 | 由于一个验证可能需要进行两次校验,所以,第一次验证成功不能移除缓存数据,第二次验证成功后移除缓存数据,防止前端反复使用一个验证数据 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 创建图片滑动数据 511 | 512 | 513 | 514 | 515 | 验证结果 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 校验数据保存接口 525 | 526 | 527 | 528 | 529 | 保存 530 | 531 | 532 | 533 | 534 | 获取 535 | 536 | 537 | 538 | 539 | 540 | 541 | 移除 542 | 543 | 544 | 545 | 546 | 547 | 548 | 内存存储校验数据 549 | 550 | 551 | 552 | 553 | 添加 554 | 555 | 556 | 557 | 558 | 559 | 560 | 获取 561 | 562 | 563 | 564 | 565 | 566 | 567 | 移除 568 | 569 | 570 | 571 | 572 | 573 | 574 | Redis存储校验数据 575 | 576 | 577 | 578 | 579 | 添加 580 | 581 | 582 | 583 | 584 | 585 | 586 | 获取 587 | 588 | 589 | 590 | 591 | 592 | 593 | 移除 594 | 595 | 596 | 597 | 598 | 599 | 600 | -------------------------------------------------------------------------------- /EasySlideVerification/Extension/SlideVerificationExtension.cs: -------------------------------------------------------------------------------- 1 | using EasySlideVerification.ImageProvider; 2 | using EasySlideVerification.Store; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace EasySlideVerification 9 | { 10 | /// 11 | /// 12 | /// 13 | public static class SlideVerificationExtension 14 | { 15 | /// 16 | /// 注册校验模块 17 | /// 18 | /// 19 | public static void AddSlideVerification(this IServiceCollection services, Action options = null) 20 | { 21 | services.AddHttpClient(); 22 | services.AddMemoryCache(); 23 | services.AddSingleton(typeof(ISlideVerifyService), typeof(SlideVerifyService)); 24 | services.AddSingleton(typeof(ISlideVerificationStore), typeof(VerificationInMemoryStore)); 25 | 26 | services.AddSingleton(typeof(IBackgroundImageService), typeof(BackgroundLocalImageService)); 27 | services.AddSingleton(typeof(ISlideImageService), typeof(SlideLocalImageService)); 28 | 29 | if (options != null) 30 | { 31 | options.Invoke(SlideVerificationOptions.Default); 32 | } 33 | } 34 | 35 | /// 36 | /// 注册校验模块,使用Redis存储 37 | /// 38 | /// 39 | public static void AddSlideVerification(this IServiceCollection services, Action redisOptions, Action options = null) 40 | { 41 | services.AddHttpClient(); 42 | services.AddSingleton(typeof(ISlideVerifyService), typeof(SlideVerifyService)); 43 | services.AddSingleton(typeof(ISlideVerificationStore), typeof(VerificationInRedisStore)); 44 | 45 | services.AddSingleton(typeof(IBackgroundImageService), typeof(BackgroundLocalImageService)); 46 | services.AddSingleton(typeof(ISlideImageService), typeof(SlideLocalImageService)); 47 | 48 | if (redisOptions != null) 49 | { 50 | redisOptions.Invoke(SlideVerificationRedisOptions.Default); 51 | } 52 | 53 | if (options != null) 54 | { 55 | options.Invoke(SlideVerificationOptions.Default); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /EasySlideVerification/Extension/SlideVerificationOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace EasySlideVerification 6 | { 7 | /// 8 | /// 9 | /// 10 | public class SlideVerificationOptions 11 | { 12 | /// 13 | /// 14 | /// 15 | public static readonly SlideVerificationOptions Default = new SlideVerificationOptions() 16 | { 17 | AcceptableDeviation = 5, 18 | Expire = new TimeSpan(0, 10, 0), 19 | }; 20 | 21 | ///// 22 | ///// 背景图 23 | ///// 24 | //public List BackgroundImages { get; set; } 25 | 26 | ///// 27 | ///// 滑块图 28 | ///// 29 | //public List SlideImages { get; set; } 30 | 31 | /// 32 | /// 可接受的误差范围 33 | /// 34 | public int AcceptableDeviation { get; set; } 35 | 36 | /// 37 | /// 右边框距离(防止由于太靠近右侧,滑动按钮无法到达) 38 | /// 39 | public int Edge { get; set; } 40 | 41 | /// 42 | /// 混淆点数量 43 | /// 44 | public int MixedCount { get; set; } 45 | 46 | /// 47 | /// 过期时间 48 | /// 49 | public TimeSpan Expire { get; set; } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /EasySlideVerification/Extension/SlideVerificationRedisOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace EasySlideVerification 6 | { 7 | /// 8 | /// 9 | /// 10 | public class SlideVerificationRedisOptions 11 | { 12 | public static readonly SlideVerificationRedisOptions Default = new SlideVerificationRedisOptions(); 13 | 14 | /// 15 | /// redis链接,如:127.0.0.1:6379 16 | /// 17 | public string Connection { get; set; } 18 | 19 | /// 20 | /// redis库索引(默认为:0) 21 | /// 22 | public int DatabaseIndex { get; set; } 23 | 24 | /// 25 | /// redis缓存key前缀,起到对缓存项分组的作用。可以为空,但建议使用 slide: 26 | /// 27 | public string KeyPrefix { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /EasySlideVerification/ISlideVerifyService.cs: -------------------------------------------------------------------------------- 1 | using EasySlideVerification.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace EasySlideVerification 7 | { 8 | /// 9 | /// 滑块校验 10 | /// 11 | public interface ISlideVerifyService 12 | { 13 | /// 14 | /// 创建图片滑动数据(图片以byte数组格式返回) 15 | /// 16 | SlideVerificationInfo Create(); 17 | 18 | /// 19 | /// 验证结果 20 | /// 21 | /// 22 | /// 23 | bool Validate(VerifyParam param); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /EasySlideVerification/ImageProvider/BackgroundLocalImageService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace EasySlideVerification.ImageProvider 6 | { 7 | /// 8 | /// 9 | /// 10 | public class BackgroundLocalImageService : LocalImageProviderBase, IBackgroundImageService 11 | { 12 | /// 13 | /// 14 | /// 15 | protected override string ImageNameFormat { get { return "bg-*.jpg"; } } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /EasySlideVerification/ImageProvider/IImageService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace EasySlideVerification.ImageProvider 6 | { 7 | /// 8 | /// 9 | /// 10 | public interface IImageService 11 | { 12 | /// 13 | /// 14 | /// 15 | /// 16 | byte[] GetRandomOne(); 17 | 18 | /// 19 | /// 20 | /// 21 | void Load(); 22 | } 23 | 24 | /// 25 | /// 26 | /// 27 | public interface ISlideImageService : IImageService { } 28 | 29 | /// 30 | /// 31 | /// 32 | public interface IBackgroundImageService : IImageService { } 33 | } 34 | -------------------------------------------------------------------------------- /EasySlideVerification/ImageProvider/LocalImageProviderBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace EasySlideVerification.ImageProvider 7 | { 8 | public abstract class LocalImageProviderBase 9 | { 10 | //图片名称 11 | abstract protected string ImageNameFormat { get; } 12 | 13 | /// 14 | /// 随机数 15 | /// 16 | Random random = new Random(); 17 | 18 | /// 19 | /// 图片列表 20 | /// 21 | List Images = new List(); 22 | 23 | /// 24 | /// 25 | /// 26 | public LocalImageProviderBase() 27 | { 28 | this.Load(); 29 | } 30 | 31 | /// 32 | /// 33 | /// 34 | /// 35 | public byte[] GetRandomOne() 36 | { 37 | int index = random.Next(this.Images.Count); 38 | return this.Images[index]; 39 | } 40 | 41 | /// 42 | /// 43 | /// 44 | public void Load() 45 | { 46 | string workDir = Directory.GetCurrentDirectory(); 47 | string imageDir = $"{workDir}{Path.DirectorySeparatorChar}App_Data{Path.DirectorySeparatorChar}Images{Path.DirectorySeparatorChar}Slide"; 48 | if (!Directory.Exists(imageDir)) 49 | { 50 | throw new DirectoryNotFoundException($"图片路径:{imageDir} 不存在."); 51 | } 52 | 53 | string[] files = Directory.GetFiles(imageDir, ImageNameFormat); 54 | for (int i = 0; i < files.Length; i++) 55 | { 56 | using (Stream stream = new FileStream(files[i], FileMode.Open, FileAccess.Read)) 57 | { 58 | byte[] image = new byte[stream.Length]; 59 | stream.Read(image, 0, image.Length); 60 | this.Images.Add(image); 61 | } 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /EasySlideVerification/ImageProvider/SlideLocalImageService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace EasySlideVerification.ImageProvider 6 | { 7 | /// 8 | /// 9 | /// 10 | public class SlideLocalImageService : LocalImageProviderBase, ISlideImageService 11 | { 12 | /// 13 | /// 14 | /// 15 | protected override string ImageNameFormat { get { return "slide-*.png"; } } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /EasySlideVerification/Model/SlideVerificationInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace EasySlideVerification.Model 6 | { 7 | /// 8 | /// 9 | /// 10 | public class SlideVerificationInfo 11 | { 12 | /// 13 | /// 14 | /// 15 | public string Key { get; set; } 16 | 17 | /// 18 | /// 背景图片(byte数组图片数据) 19 | /// 20 | public byte[] BackgroundImg { get; set; } 21 | 22 | /// 23 | /// 滑块图片(byte数组图片数据) 24 | /// 25 | public byte[] SlideImg { get; set; } 26 | 27 | /// 28 | /// 29 | /// 30 | public int PositionX { get; set; } 31 | 32 | /// 33 | /// 34 | /// 35 | public int PositionY { get; set; } 36 | 37 | /// 38 | /// 39 | /// 40 | public int BgHeight { get; set; } 41 | 42 | /// 43 | /// 44 | /// 45 | public int BgWidth { get; set; } 46 | 47 | /// 48 | /// 49 | /// 50 | public int SlideHeight { get; set; } 51 | 52 | /// 53 | /// 54 | /// 55 | public int SlideWidth { get; set; } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /EasySlideVerification/Model/SlideVerificationParam.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace EasySlideVerification.Model 6 | { 7 | /// 8 | /// 9 | /// 10 | public class SlideVerificationParam 11 | { 12 | /// 13 | /// 背景图片原图 14 | /// 15 | public byte[] BackgroundImage { get; set; } 16 | 17 | /// 18 | /// 滑块图 19 | /// 20 | public byte[] SlideImage { get; set; } 21 | 22 | /// 23 | /// 24 | /// 25 | public int Edge { get; set; } 26 | 27 | /// 28 | /// 29 | /// 30 | public int MixedCount { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /EasySlideVerification/Model/SlideVerificationPlainInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace EasySlideVerification.Model 6 | { 7 | /// 8 | /// 9 | /// 10 | public class SlideVerificationPlainInfo 11 | { 12 | /// 13 | /// 14 | /// 15 | public string Key { get; set; } 16 | 17 | /// 18 | /// 背景图片 19 | /// 20 | public string BackgroundImg { get; set; } 21 | 22 | /// 23 | /// 滑块图片 24 | /// 25 | public string SlideImg { get; set; } 26 | 27 | /// 28 | /// 29 | /// 30 | public int PositionX { get; set; } 31 | 32 | /// 33 | /// 34 | /// 35 | public int PositionY { get; set; } 36 | 37 | /// 38 | /// 背景图片高度 39 | /// 40 | public int BgHeight { get; set; } 41 | 42 | /// 43 | /// 背景图片宽度 44 | /// 45 | public int BgWidth { get; set; } 46 | 47 | /// 48 | /// 49 | /// 50 | public int SlideHeight { get; set; } 51 | 52 | /// 53 | /// 54 | /// 55 | public int SlideWidth { get; set; } 56 | 57 | /// 58 | /// 对象拷贝 59 | /// 60 | /// 61 | /// 62 | public static SlideVerificationPlainInfo From(SlideVerificationInfo data) 63 | { 64 | SlideVerificationPlainInfo result = null; 65 | if (data != null) 66 | { 67 | result = new SlideVerificationPlainInfo() 68 | { 69 | Key = data.Key, 70 | PositionX = data.PositionX, 71 | PositionY = data.PositionY, 72 | BgHeight = data.BgHeight, 73 | BgWidth = data.BgWidth, 74 | SlideHeight = data.SlideHeight, 75 | SlideWidth = data.SlideWidth, 76 | }; 77 | } 78 | return result; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /EasySlideVerification/Model/VerifyParam.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace EasySlideVerification.Model 6 | { 7 | /// 8 | /// 校验参数 9 | /// 10 | public class VerifyParam 11 | { 12 | /// 13 | /// 14 | /// 15 | public string Key { get; set; } 16 | 17 | /// 18 | /// 19 | /// 20 | public int PositionX { get; set; } 21 | 22 | /// 23 | /// 24 | /// 25 | public int PositionY { get; set; } 26 | 27 | /// 28 | /// 是否校验成功后移除缓存 29 | /// 由于一个验证可能需要进行两次校验,所以,第一次验证成功不能移除缓存数据,第二次验证成功后移除缓存数据,防止前端反复使用一个验证数据 30 | /// 31 | public bool RemoveIfSuccess { get; set; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /EasySlideVerification/SlideVerifyService.cs: -------------------------------------------------------------------------------- 1 | using EasySlideVerification.Common; 2 | using EasySlideVerification.ImageProvider; 3 | using EasySlideVerification.Model; 4 | using EasySlideVerification.Store; 5 | using System; 6 | using System.Drawing; 7 | using System.Drawing.Drawing2D; 8 | using System.Drawing.Imaging; 9 | using System.IO; 10 | using System.Net.Http; 11 | using System.Text; 12 | 13 | namespace EasySlideVerification 14 | { 15 | /// 16 | /// 17 | /// 18 | public class SlideVerifyService : ISlideVerifyService 19 | { 20 | IHttpClientFactory httpClientFactory; 21 | ISlideVerificationStore store; 22 | IBackgroundImageService backgrouondImageService; 23 | ISlideImageService slideImageService; 24 | 25 | public SlideVerifyService( 26 | IHttpClientFactory httpClientFactory, 27 | ISlideVerificationStore store, 28 | IBackgroundImageService backgrouondImageService, 29 | ISlideImageService slideImageService) 30 | { 31 | this.httpClientFactory = httpClientFactory; 32 | this.store = store; 33 | this.backgrouondImageService = backgrouondImageService; 34 | this.slideImageService = slideImageService; 35 | } 36 | 37 | /// 38 | /// 创建图片滑动数据 39 | /// 40 | public SlideVerificationInfo Create() 41 | { 42 | SlideVerificationParam param = new SlideVerificationParam() 43 | { 44 | BackgroundImage = this.backgrouondImageService.GetRandomOne(), 45 | SlideImage = this.slideImageService.GetRandomOne(), 46 | Edge = SlideVerificationOptions.Default.Edge, 47 | MixedCount = SlideVerificationOptions.Default.MixedCount, 48 | }; 49 | SlideVerificationInfo result = SlideVerificationCreater.Instance.Create(param); 50 | 51 | this.store.Add(result, SlideVerificationOptions.Default.Expire); 52 | 53 | return result; 54 | } 55 | 56 | /// 57 | /// 验证结果 58 | /// 59 | /// 60 | /// 61 | /// 62 | /// 63 | public bool Validate(VerifyParam param) 64 | { 65 | var data = this.store.Get(param.Key); 66 | if (data == null) return false; 67 | 68 | int accept = SlideVerificationOptions.Default.AcceptableDeviation; 69 | bool success = param.PositionX > data.PositionX - accept && param.PositionX < data.PositionX + accept 70 | && param.PositionY > data.PositionY - accept && param.PositionY < data.PositionY + accept; 71 | //验证成功,移除缓存 72 | if (success && param.RemoveIfSuccess) 73 | { 74 | this.store.Remove(param.Key); 75 | } 76 | 77 | return success; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /EasySlideVerification/Store/ISlideVerificationStore.cs: -------------------------------------------------------------------------------- 1 | using EasySlideVerification.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace EasySlideVerification.Store 7 | { 8 | /// 9 | /// 校验数据保存接口 10 | /// 11 | public interface ISlideVerificationStore 12 | { 13 | /// 14 | /// 保存 15 | /// 16 | void Add(SlideVerificationInfo data, TimeSpan expire); 17 | 18 | /// 19 | /// 获取 20 | /// 21 | /// 22 | /// 23 | SlideVerificationInfo Get(string key); 24 | 25 | /// 26 | /// 移除 27 | /// 28 | /// 29 | /// 30 | void Remove(string key); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /EasySlideVerification/Store/VerificationInMemoryStore.cs: -------------------------------------------------------------------------------- 1 | using EasySlideVerification.Model; 2 | using Microsoft.Extensions.Caching.Memory; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace EasySlideVerification.Store 8 | { 9 | /// 10 | /// 内存存储校验数据 11 | /// 12 | public class VerificationInMemoryStore : ISlideVerificationStore 13 | { 14 | IMemoryCache store; 15 | 16 | public VerificationInMemoryStore(IMemoryCache store) 17 | { 18 | this.store = store; 19 | } 20 | 21 | /// 22 | /// 添加 23 | /// 24 | /// 25 | /// 26 | public void Add(SlideVerificationInfo data, TimeSpan expire) 27 | { 28 | this.store.Set(data.Key, data, expire); 29 | } 30 | 31 | /// 32 | /// 获取 33 | /// 34 | /// 35 | /// 36 | public SlideVerificationInfo Get(string key) 37 | { 38 | return this.store.Get(key); 39 | } 40 | 41 | /// 42 | /// 移除 43 | /// 44 | /// 45 | /// 46 | public void Remove(string key) 47 | { 48 | this.store.Remove(key); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /EasySlideVerification/Store/VerificationInRedisStore.cs: -------------------------------------------------------------------------------- 1 | using EasySlideVerification.Common; 2 | using EasySlideVerification.Model; 3 | using StackExchange.Redis; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace EasySlideVerification.Store 10 | { 11 | /// 12 | /// Redis存储校验数据 13 | /// 14 | public class VerificationInRedisStore : ISlideVerificationStore 15 | { 16 | IDatabase store; 17 | 18 | public VerificationInRedisStore() 19 | { 20 | IConnectionMultiplexer connection = ConnectionMultiplexer.Connect(SlideVerificationRedisOptions.Default.Connection); 21 | this.store = connection.GetDatabase(SlideVerificationRedisOptions.Default.DatabaseIndex); 22 | } 23 | 24 | /// 25 | /// 添加 26 | /// 27 | /// 28 | /// 29 | public void Add(SlideVerificationInfo data, TimeSpan expire) 30 | { 31 | HashEntry[] entries = new HashEntry[] { 32 | new HashEntry("BackgroudImage",data.BackgroundImg), 33 | new HashEntry("SlideImage",data.SlideImg), 34 | new HashEntry("OffsetX",data.PositionX), 35 | new HashEntry("OffsetY",data.PositionY), 36 | }; 37 | this.store.HashSet($"{SlideVerificationRedisOptions.Default.KeyPrefix}{data.Key}", entries); 38 | this.store.KeyExpire($"{SlideVerificationRedisOptions.Default.KeyPrefix}{data.Key}", expire); 39 | } 40 | 41 | /// 42 | /// 获取 43 | /// 44 | /// 45 | /// 46 | public SlideVerificationInfo Get(string key) 47 | { 48 | SlideVerificationInfo result = null; 49 | HashEntry[] entries = this.store.HashGetAll($"{SlideVerificationRedisOptions.Default.KeyPrefix}{key}"); 50 | if (entries != null && entries.Length > 0) 51 | { 52 | result = new SlideVerificationInfo(); 53 | result.BackgroundImg = entries.First(a => a.Name == "BackgroudImage").Value; 54 | result.SlideImg = entries.First(a => a.Name == "SlideImage").Value; 55 | result.PositionX = entries.First(a => a.Name == "OffsetX").Value.ToString().ToInt(); 56 | result.PositionY = entries.First(a => a.Name == "OffsetY").Value.ToString().ToInt(); 57 | } 58 | 59 | return result; 60 | } 61 | 62 | /// 63 | /// 移除 64 | /// 65 | /// 66 | /// 67 | public void Remove(string key) 68 | { 69 | this.store.KeyDelete($"{SlideVerificationRedisOptions.Default.KeyPrefix}{key}"); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /EasySlideVerificationDemo/.env.development: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'development' 3 | 4 | # base api 5 | VUE_APP_BASE_API = 'http://localhost:52940/' 6 | -------------------------------------------------------------------------------- /EasySlideVerificationDemo/.env.production: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'production' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '' 6 | 7 | -------------------------------------------------------------------------------- /EasySlideVerificationDemo/EasySlideVerificationDemo.njsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 14.0 4 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 5 | EasySlideVerificationDemo 6 | EasySlideVerificationDemo 7 | Release|Any CPU 8 | 9 | 10 | 11 | Debug 12 | 2.0 13 | 98bb4a9b-7da8-4661-82ce-c279c53c0798 14 | . 15 | node_modules\@vue\cli-service\bin\vue-cli-service.js 16 | 17 | 18 | . 19 | . 20 | v4.0 21 | {3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD} 22 | 1337 23 | true 24 | serve 25 | 26 | 27 | true 28 | 29 | 30 | true 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Code 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | npm run build 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | False 77 | True 78 | 0 79 | / 80 | http://localhost:48022/ 81 | False 82 | True 83 | http://localhost:1337 84 | False 85 | 86 | 87 | 88 | 89 | 90 | 91 | CurrentPage 92 | True 93 | False 94 | False 95 | False 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | False 105 | False 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /EasySlideVerificationDemo/README.md: -------------------------------------------------------------------------------- 1 | # EasySlideVerificationDemo 2 | 3 | 4 | -------------------------------------------------------------------------------- /EasySlideVerificationDemo/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | }; 6 | -------------------------------------------------------------------------------- /EasySlideVerificationDemo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "easy-slide-verification-demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vue-cli-service serve", 7 | "serve": "vue-cli-service serve", 8 | "build": "vue-cli-service build", 9 | "lint": "vue-cli-service lint" 10 | }, 11 | "description": "EasySlideVerificationDemo", 12 | "author": { 13 | "name": "" 14 | }, 15 | "dependencies": { 16 | "axios": "^0.20.0", 17 | "vue": "2.5.17" 18 | }, 19 | "devDependencies": { 20 | "@vue/cli-plugin-babel": "3.0.4", 21 | "@vue/cli-plugin-eslint": "3.0.4", 22 | "@vue/cli-service": "3.0.4", 23 | "eslint": "5.6.0", 24 | "eslint-plugin-vue": "4.7.1", 25 | "vue-template-compiler": "2.5.17" 26 | }, 27 | "eslintConfig": { 28 | "root": true, 29 | "env": { 30 | "node": true 31 | }, 32 | "extends": [ 33 | "plugin:vue/essential", 34 | "eslint:recommended" 35 | ], 36 | "rules": {}, 37 | "parserOptions": { 38 | "parser": "babel-eslint" 39 | } 40 | }, 41 | "postcss": { 42 | "plugins": { 43 | "autoprefixer": {} 44 | } 45 | }, 46 | "browserslist": [ 47 | "> 1%", 48 | "last 2 versions", 49 | "not ie <= 8" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /EasySlideVerificationDemo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonosun/EasySlideVerification/4a437ff92c3f4463cc697126127526bf3186b953/EasySlideVerificationDemo/public/favicon.ico -------------------------------------------------------------------------------- /EasySlideVerificationDemo/public/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | EasySlideVerificationDemo 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /EasySlideVerificationDemo/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 20 | -------------------------------------------------------------------------------- /EasySlideVerificationDemo/src/api/VerificationDemoApi.js: -------------------------------------------------------------------------------- 1 | import request from "@/utils/request.js" 2 | 3 | export function getVerification() { 4 | return request({ 5 | url: '/home/getVerification', 6 | method: 'get' 7 | }) 8 | } 9 | 10 | export function verify(param) { 11 | return request({ 12 | url: '/home/verify', 13 | method: 'post', 14 | data: param 15 | }) 16 | } -------------------------------------------------------------------------------- /EasySlideVerificationDemo/src/components/Home.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonosun/EasySlideVerification/4a437ff92c3f4463cc697126127526bf3186b953/EasySlideVerificationDemo/src/components/Home.vue -------------------------------------------------------------------------------- /EasySlideVerificationDemo/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import App from './App.vue'; 3 | 4 | Vue.config.productionTip = true; 5 | 6 | new Vue({ 7 | render: h => h(App) 8 | }).$mount('#app'); 9 | -------------------------------------------------------------------------------- /EasySlideVerificationDemo/src/slider/slider.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonosun/EasySlideVerification/4a437ff92c3f4463cc697126127526bf3186b953/EasySlideVerificationDemo/src/slider/slider.vue -------------------------------------------------------------------------------- /EasySlideVerificationDemo/src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | // create an axios instance 4 | const service = axios.create({ 5 | baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url 6 | // withCredentials: true, // send cookies when cross-domain requests 7 | timeout: 10000 // request timeout 8 | }) 9 | 10 | // request interceptor 11 | service.interceptors.request.use( 12 | config => { 13 | // do something before request is sent 14 | 15 | // let each request carry token 16 | // ['X-Token'] is a custom headers key 17 | // please modify it according to the actual situation 18 | // config.headers['token'] = getToken() 19 | return config 20 | }, 21 | error => { 22 | // do something with request error 23 | // console.log(error) // for debug 24 | return Promise.reject(error) 25 | } 26 | ) 27 | 28 | // response interceptor 29 | service.interceptors.response.use( 30 | /** 31 | * If you want to get http information such as headers or status 32 | * Please return response => response 33 | */ 34 | 35 | /** 36 | * Determine the request status by custom code 37 | * Here is just an example 38 | * You can also judge the status by HTTP Status Code 39 | */ 40 | response => { 41 | const res = response.data 42 | return res; 43 | }, 44 | error => { 45 | // console.log('err' + error) // for debug 46 | return Promise.reject(error) 47 | } 48 | ) 49 | 50 | export default service 51 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/App_Data/Images/Slide/bg-s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonosun/EasySlideVerification/4a437ff92c3f4463cc697126127526bf3186b953/EasySlideVerificationDemoServer/App_Data/Images/Slide/bg-s.jpg -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/App_Data/Images/Slide/slide-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonosun/EasySlideVerification/4a437ff92c3f4463cc697126127526bf3186b953/EasySlideVerificationDemoServer/App_Data/Images/Slide/slide-01.png -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Mvc; 7 | using EasySlideVerificationDemoServer.Models; 8 | using EasySlideVerification; 9 | using EasySlideVerification.Model; 10 | using EasySlideVerification.Common; 11 | using System.Drawing.Imaging; 12 | 13 | namespace EasySlideVerificationDemoServer.Controllers 14 | { 15 | /// 16 | /// 17 | /// 18 | public class HomeController : Controller 19 | { 20 | ISlideVerifyService verifyService; 21 | 22 | public HomeController(ISlideVerifyService verifyService) 23 | { 24 | this.verifyService = verifyService; 25 | } 26 | 27 | 28 | public IActionResult Index() 29 | { 30 | return View(); 31 | } 32 | 33 | /// 34 | /// 创建图片滑动数据 35 | /// 36 | /// 37 | public ActionResult GetVerification() 38 | { 39 | var data = this.verifyService.Create(); 40 | var result = ConvertToBase64PlainInfo(data); 41 | return result; 42 | } 43 | 44 | /// 45 | /// 校验 46 | /// 47 | /// 48 | /// 49 | public ActionResult Verify([FromBody]VerifyParam param) 50 | { 51 | return this.verifyService.Validate(param); 52 | } 53 | 54 | /// 55 | /// 图片由byte[]转换为base64字符串 56 | /// 57 | private SlideVerificationPlainInfo ConvertToBase64PlainInfo(SlideVerificationInfo data) 58 | { 59 | SlideVerificationPlainInfo result = SlideVerificationPlainInfo.From(data); 60 | if (result != null) 61 | { 62 | result.BackgroundImg = ImageUtil.ImageToBase64(data.BackgroundImg, ImageFormat.Jpeg); 63 | result.SlideImg = ImageUtil.ImageToBase64(data.SlideImg, ImageFormat.Png); 64 | //PositionX 不能输出到前端 65 | result.PositionX = 0; 66 | }; 67 | 68 | return result; 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Dockerfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonosun/EasySlideVerification/4a437ff92c3f4463cc697126127526bf3186b953/EasySlideVerificationDemoServer/Dockerfile -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Dockerfile2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonosun/EasySlideVerification/4a437ff92c3f4463cc697126127526bf3186b953/EasySlideVerificationDemoServer/Dockerfile2 -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/EasySlideVerificationDemoServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | Linux 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | PreserveNewest 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EasySlideVerificationDemoServer.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace EasySlideVerificationDemoServer 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Properties/PublishProfiles/linux.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | FileSystem 9 | FileSystem 10 | Release 11 | Any CPU 12 | 13 | True 14 | False 15 | netcoreapp2.1 16 | linux-x64 17 | 77d9e41a-d7a7-468d-ad50-fd5760f19a96 18 | true 19 | <_IsPortable>false 20 | bin\Release\netcoreapp2.1\linux\ 21 | False 22 | 23 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Properties/PublishProfiles/windows.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | FileSystem 9 | FileSystem 10 | Release 11 | Any CPU 12 | 13 | True 14 | False 15 | netcoreapp2.1 16 | win-x64 17 | 77d9e41a-d7a7-468d-ad50-fd5760f19a96 18 | true 19 | <_IsPortable>false 20 | bin\Release\netcoreapp2.1\publish\ 21 | False 22 | 23 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:52940", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "EasySlideVerificationDemoServer": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:5000" 25 | }, 26 | "Docker": { 27 | "commandName": "Docker", 28 | "launchBrowser": true, 29 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}" 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using EasySlideVerification; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.AspNetCore.Http; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | 13 | namespace EasySlideVerificationDemoServer 14 | { 15 | public class Startup 16 | { 17 | public Startup(IConfiguration configuration) 18 | { 19 | Configuration = configuration; 20 | } 21 | 22 | public IConfiguration Configuration { get; } 23 | 24 | // This method gets called by the runtime. Use this method to add services to the container. 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | //注册滑动校验 28 | services.AddSlideVerification(options=> { 29 | //可接受的误差范围 30 | options.AcceptableDeviation = 5; 31 | //右边框距离(防止由于太靠近右侧,自定义的滑动按钮无法到达) 32 | options.Edge = 0; 33 | //数据过期时间 34 | options.Expire = new TimeSpan(0, 5, 0); 35 | //混淆点数量 36 | options.MixedCount = 0; 37 | }); 38 | 39 | ////注册滑动校验,使用Redis缓存 40 | //services.AddSlideVerification( 41 | // redisOptions => 42 | // { 43 | // redisOptions.Connection = "127.0.0.1:6379"; 44 | // redisOptions.DatabaseIndex = 0; 45 | // redisOptions.KeyPrefix = "slide:"; 46 | // }, 47 | // options => { 48 | // }); 49 | 50 | services.Configure(options => 51 | { 52 | // This lambda determines whether user consent for non-essential cookies is needed for a given request. 53 | options.CheckConsentNeeded = context => true; 54 | options.MinimumSameSitePolicy = SameSiteMode.None; 55 | }); 56 | 57 | 58 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 59 | } 60 | 61 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 62 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 63 | { 64 | if (env.IsDevelopment()) 65 | { 66 | app.UseDeveloperExceptionPage(); 67 | } 68 | else 69 | { 70 | app.UseExceptionHandler("/Home/Error"); 71 | } 72 | 73 | app.UseStaticFiles(); 74 | app.UseCookiePolicy(); 75 | app.UseCors(policy => 76 | { 77 | policy.WithOrigins("*") 78 | .AllowAnyHeader() 79 | .AllowAnyMethod() 80 | .AllowAnyOrigin(); 81 | }); 82 | 83 | app.UseMvc(routes => 84 | { 85 | routes.MapRoute( 86 | name: "default", 87 | template: "{controller=Home}/{action=Index}/{id?}"); 88 | }); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 | 5 |
6 |
7 |

Hello World

8 | 9 |
10 |
11 | @section Style{ 12 | 13 | } 14 | @section Scripts{ 15 | 16 | 53 | } -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. 22 |

23 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - EasySlideVerificationDemoServer 7 | 8 | 9 | @RenderSection("Style", required: false) 10 | 11 | 12 | 13 |
14 | @RenderBody() 15 | 16 |
17 |

© 2020 - EasySlideVerificationDemoServer

18 |
19 |
20 | 21 | 22 | 23 | 24 | @RenderSection("Scripts", required: false) 25 | 26 | 27 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using EasySlideVerificationDemoServer 2 | @using EasySlideVerificationDemoServer.Models 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification\ 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | body { 4 | padding-top: 50px; 5 | padding-bottom: 20px; 6 | } 7 | 8 | /* Wrapping element */ 9 | /* Set some basic padding to keep content from hitting the edges */ 10 | .body-content { 11 | padding-left: 15px; 12 | padding-right: 15px; 13 | } 14 | 15 | /* Carousel */ 16 | .carousel-caption p { 17 | font-size: 20px; 18 | line-height: 1.4; 19 | } 20 | 21 | /* Make .svg files in the carousel display properly in older browsers */ 22 | .carousel-inner .item img[src$=".svg"] { 23 | width: 100%; 24 | } 25 | 26 | /* QR code generator */ 27 | #qrCode { 28 | margin: 15px; 29 | } 30 | 31 | /* Hide/rearrange for smaller screens */ 32 | @media screen and (max-width: 767px) { 33 | /* Hide captions */ 34 | .carousel-caption { 35 | display: none; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonosun/EasySlideVerification/4a437ff92c3f4463cc697126127526bf3186b953/EasySlideVerificationDemoServer/wwwroot/favicon.ico -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonosun/EasySlideVerification/4a437ff92c3f4463cc697126127526bf3186b953/EasySlideVerificationDemoServer/wwwroot/js/site.min.js -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/wwwroot/lib/jquery/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "dist/jquery.js", 4 | "license": "MIT", 5 | "ignore": [ 6 | "package.json" 7 | ], 8 | "keywords": [ 9 | "jquery", 10 | "javascript", 11 | "browser", 12 | "library" 13 | ], 14 | "homepage": "https://github.com/jquery/jquery-dist", 15 | "version": "3.3.1", 16 | "_release": "3.3.1", 17 | "_resolution": { 18 | "type": "version", 19 | "tag": "3.3.1", 20 | "commit": "9e8ec3d10fad04748176144f108d7355662ae75e" 21 | }, 22 | "_source": "https://github.com/jquery/jquery-dist.git", 23 | "_target": "^3.3.1", 24 | "_originalSource": "jquery", 25 | "_direct": true 26 | } -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /EasySlideVerificationDemoServer/wwwroot/lib/jquery/dist/jquery.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ 2 | !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w("