├── .gitignore ├── BuildMonitor.png ├── BuildMonitor.sln ├── BuildMonitor ├── App_Data │ └── Settings.xml ├── BuildMonitor.csproj ├── Helpers │ ├── BuildMonitorModelHandlerBase.cs │ ├── CustomBuildMonitorModelHandler.cs │ ├── DefaultBuildMonitorModelHandler.cs │ ├── IBuildMonitorModelHandler.cs │ └── RequestHelper.cs ├── Models │ ├── Index │ │ ├── Build.cs │ │ ├── BuildJson.cs │ │ ├── BuildMonitorViewModel.cs │ │ ├── BuildStatus.cs │ │ ├── BuildsJson.cs │ │ ├── HiddenJob.cs │ │ ├── Project.cs │ │ └── Settings │ │ │ ├── Group.cs │ │ │ ├── Job.cs │ │ │ └── Settings.cs │ ├── SecretTeamCitySettings.cs │ └── TeamCitySettings.cs ├── Pages │ ├── Error.cshtml │ ├── Error.cshtml.cs │ ├── Index.cshtml │ ├── Index.cshtml.cs │ ├── Privacy.cshtml │ ├── Privacy.cshtml.cs │ ├── Shared │ │ ├── _CookieConsentPartial.cshtml │ │ ├── _Layout.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── _BuildItem.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── appsettings.Development.json ├── appsettings.json └── wwwroot │ ├── css │ ├── bootstrap.min.css │ └── site.css │ ├── favicon.ico │ ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ ├── kenzo.eot │ ├── kenzo.otf │ ├── kenzo.ttf │ └── kenzo.woff │ ├── js │ └── site.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | [Oo]bj 2 | [Bb]in 3 | [Rr]ealse 4 | [Dd]ebug 5 | .vs/* 6 | ./Resharp* 7 | Thumbs.db 8 | cmd.exe 9 | Git Bash.lnk 10 | *.docstates.suo 11 | *.suo 12 | *.user 13 | [Tt]est[Rr]esults 14 | *.dep 15 | *.aps 16 | *.vbw 17 | *.obj 18 | *.ncb 19 | *.plg 20 | *.bsc 21 | *.ilk 22 | *.exp 23 | *.sbr 24 | *.opt 25 | *.pdb 26 | *.idb 27 | *.pch 28 | *.res 29 | *.*.sw* 30 | .DS_Store 31 | node_modules 32 | .idea 33 | *.iml 34 | npm-debug.log 35 | *.psess 36 | *.vsp 37 | *.ipr 38 | *.iws 39 | /.project 40 | /packages -------------------------------------------------------------------------------- /BuildMonitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohanGl/TeamCity_BuildMonitor/1d83847444fe0ffdea576d5bccde0958861b34e0/BuildMonitor.png -------------------------------------------------------------------------------- /BuildMonitor.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.271 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildMonitor", "BuildMonitor\BuildMonitor.csproj", "{A2A86E1D-549A-4C5E-BB41-C64232E32CA5}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {A2A86E1D-549A-4C5E-BB41-C64232E32CA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {A2A86E1D-549A-4C5E-BB41-C64232E32CA5}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {A2A86E1D-549A-4C5E-BB41-C64232E32CA5}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {A2A86E1D-549A-4C5E-BB41-C64232E32CA5}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {FD6618F5-07BF-4F6B-BC54-396AC44C5325} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /BuildMonitor/App_Data/Settings.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /BuildMonitor/BuildMonitor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | InProcess 6 | 632c561f-3ed7-4d6d-80b2-d7a23b8c65cd 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | PreserveNewest 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /BuildMonitor/Helpers/BuildMonitorModelHandlerBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using BuildMonitor.Models; 5 | using BuildMonitor.Models.Index; 6 | using Newtonsoft.Json; 7 | 8 | namespace BuildMonitor.Helpers 9 | { 10 | public abstract class BuildMonitorModelHandlerBase : IBuildMonitorModelHandler 11 | { 12 | protected TeamCitySettings teamcitySettings; 13 | 14 | protected Dictionary runningBuilds; 15 | 16 | protected dynamic projectsJson; 17 | protected dynamic buildTypesJson; 18 | protected dynamic buildQueueJson; 19 | protected dynamic buildStatusJson; 20 | 21 | public void Initialize(TeamCitySettings settings) 22 | { 23 | teamcitySettings = new TeamCitySettings 24 | { 25 | UserName = settings.UserName, 26 | Password = settings.Password, 27 | Projects = settings.Url + settings.Projects, 28 | BuildTypes = settings.Url + settings.BuildTypes, 29 | RunningBuilds = settings.Url + settings.RunningBuilds, 30 | BuildStatus = settings.Url + settings.BuildStatus, 31 | BuildQueue = settings.Url + settings.BuildQueue 32 | }; 33 | } 34 | 35 | protected void GetTeamCityBuildsJson() 36 | { 37 | var projectsJsonString = RequestHelper.GetJson(teamcitySettings.Projects); 38 | projectsJson = JsonConvert.DeserializeObject(projectsJsonString); 39 | 40 | var buildTypesJsonString = RequestHelper.GetJson(teamcitySettings.BuildTypes); 41 | buildTypesJson = JsonConvert.DeserializeObject(buildTypesJsonString); 42 | 43 | var buildQueueJsonString = RequestHelper.GetJson(teamcitySettings.BuildQueue); 44 | buildQueueJson = buildQueueJsonString != null ? JsonConvert.DeserializeObject(buildQueueJsonString) : null; 45 | 46 | UpdateRunningBuilds(); 47 | } 48 | 49 | private void UpdateRunningBuilds() 50 | { 51 | try 52 | { 53 | runningBuilds = new Dictionary(); 54 | 55 | var runningBuildsJsonString = RequestHelper.GetJson(teamcitySettings.RunningBuilds); 56 | var runningBuildsJson = runningBuildsJsonString != null ? JsonConvert.DeserializeObject(runningBuildsJsonString) : null; 57 | 58 | var count = (int)runningBuildsJson.count; 59 | for (int i = 0; i < count; i++) 60 | { 61 | var buildJson = runningBuildsJson.build[i]; 62 | 63 | var buildId = (string)buildJson.buildTypeId; 64 | var url = teamcitySettings.Url + (string)buildJson.href; 65 | 66 | var buildStatusJsonString = RequestHelper.GetJson(url); 67 | var buildStatusJson = JsonConvert.DeserializeObject(buildStatusJsonString ?? string.Empty); 68 | 69 | runningBuilds.Add(buildId, buildStatusJson); 70 | } 71 | } 72 | catch 73 | { 74 | } 75 | } 76 | 77 | protected void UpdateBuildStatusFromRunningBuildJson(string buildId) 78 | { 79 | buildStatusJson = runningBuilds[buildId]; 80 | } 81 | 82 | protected BuildStatus GetBuildStatusForRunningBuild(string buildId) 83 | { 84 | if (runningBuilds.ContainsKey(buildId)) 85 | { 86 | return BuildStatus.Running; 87 | } 88 | 89 | if (buildStatusJson == null) 90 | { 91 | return BuildStatus.None; 92 | } 93 | 94 | switch ((string)buildStatusJson.status) 95 | { 96 | case "SUCCESS": 97 | return BuildStatus.Success; 98 | 99 | case "FAILURE": 100 | return BuildStatus.Failure; 101 | 102 | case "ERROR": 103 | return BuildStatus.Error; 104 | 105 | default: 106 | return BuildStatus.None; 107 | } 108 | } 109 | 110 | protected string[] GetRunningBuildBranchAndProgress(string buildId) 111 | { 112 | var result = new[] 113 | { 114 | string.Empty, 115 | string.Empty 116 | }; 117 | 118 | try 119 | { 120 | result[0] = (string)runningBuilds[buildId].branchName ?? "default"; 121 | 122 | var percentage = (string)runningBuilds[buildId].percentageComplete; 123 | result[1] = !string.IsNullOrWhiteSpace(percentage) ? percentage + "%" : "0%"; 124 | } 125 | catch 126 | { 127 | } 128 | 129 | return result; 130 | } 131 | 132 | public abstract BuildMonitorViewModel GetModel(); 133 | 134 | protected string GetLastRunText() 135 | { 136 | const int second = 1; 137 | const int minute = 60 * second; 138 | const int hour = 60 * minute; 139 | const int day = 24 * hour; 140 | const int month = 30 * day; 141 | 142 | try 143 | { 144 | var dateTime = DateTime.ParseExact((string)buildStatusJson.startDate, "yyyyMMdd'T'HHmmsszzz", CultureInfo.InvariantCulture); 145 | 146 | var timeSpan = new TimeSpan(DateTime.Now.Ticks - dateTime.Ticks); 147 | double delta = Math.Abs(timeSpan.TotalSeconds); 148 | 149 | if (delta < 1 * minute) 150 | { 151 | return timeSpan.Seconds == 1 ? "one second ago" : timeSpan.Seconds + " seconds ago"; 152 | } 153 | if (delta < 2 * minute) 154 | { 155 | return "a minute ago"; 156 | } 157 | if (delta < 45 * minute) 158 | { 159 | return timeSpan.Minutes + " minutes ago"; 160 | } 161 | if (delta < 90 * minute) 162 | { 163 | return "an hour ago"; 164 | } 165 | if (delta < 24 * hour) 166 | { 167 | return timeSpan.Hours + " hours ago"; 168 | } 169 | if (delta < 48 * hour) 170 | { 171 | return "yesterday"; 172 | } 173 | if (delta < 30 * day) 174 | { 175 | return timeSpan.Days + " days ago"; 176 | } 177 | 178 | if (delta < 12 * month) 179 | { 180 | int months = Convert.ToInt32(Math.Floor((double)timeSpan.Days / 30)); 181 | return months <= 1 ? "one month ago" : months + " months ago"; 182 | } 183 | else 184 | { 185 | int years = Convert.ToInt32(Math.Floor((double)timeSpan.Days / 365)); 186 | return years <= 1 ? "one year ago" : years + " years ago"; 187 | } 188 | } 189 | catch 190 | { 191 | return string.Empty; 192 | } 193 | } 194 | } 195 | } -------------------------------------------------------------------------------- /BuildMonitor/Helpers/CustomBuildMonitorModelHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Xml.Serialization; 4 | using BuildMonitor.Models.Index; 5 | using BuildMonitor.Models.Index.Settings; 6 | using Newtonsoft.Json; 7 | 8 | namespace BuildMonitor.Helpers 9 | { 10 | public class CustomBuildMonitorModelHandler : BuildMonitorModelHandlerBase 11 | { 12 | private Settings settings; 13 | 14 | public CustomBuildMonitorModelHandler() 15 | { 16 | InitializeSettings(); 17 | } 18 | 19 | private void InitializeSettings() 20 | { 21 | if (settings != null) 22 | { 23 | return; 24 | } 25 | 26 | var path = AppDomain.CurrentDomain.BaseDirectory + "/App_Data/Settings.xml"; 27 | using (var reader = new StreamReader(path)) 28 | { 29 | var serializer = new XmlSerializer(typeof(Settings)); 30 | settings = (Settings)serializer.Deserialize(reader); 31 | } 32 | } 33 | 34 | public override BuildMonitorViewModel GetModel() 35 | { 36 | var model = new BuildMonitorViewModel(); 37 | 38 | GetTeamCityBuildsJson(); 39 | 40 | foreach (var group in settings.Groups) 41 | { 42 | var project = new Project(); 43 | project.Name = group.Name; 44 | 45 | AddBuilds(ref project, group); 46 | 47 | model.Projects.Add(project); 48 | } 49 | 50 | return model; 51 | } 52 | 53 | private void AddBuilds(ref Project project, Group group) 54 | { 55 | foreach (var job in group.Jobs) 56 | { 57 | var buildTypeJson = GetJsonBuildTypeById(job.Id); 58 | 59 | var build = new Build(); 60 | build.Id = buildTypeJson.id; 61 | build.Name = job.Text ?? buildTypeJson.name; 62 | 63 | var url = string.Format(base.teamcitySettings.BuildStatus, build.Id); 64 | var buildStatusJsonString = RequestHelper.GetJson(url); 65 | buildStatusJson = JsonConvert.DeserializeObject(buildStatusJsonString ?? string.Empty); 66 | 67 | build.Branch = (buildStatusJson != null) ? (buildStatusJson.branchName ?? "default") : "unknown"; 68 | build.Status = GetBuildStatusForRunningBuild(build.Id); 69 | 70 | if (build.Status == BuildStatus.Running) 71 | { 72 | UpdateBuildStatusFromRunningBuildJson(build.Id); 73 | } 74 | 75 | build.UpdatedBy = GetUpdatedBy(); 76 | build.LastRunText = GetLastRunText(); 77 | build.IsQueued = IsBuildQueued(build.Id); 78 | 79 | if (build.Status == BuildStatus.Running) 80 | { 81 | var result = GetRunningBuildBranchAndProgress(build.Id); 82 | build.Branch = result[0]; 83 | build.Progress = result[1]; 84 | } 85 | else 86 | { 87 | build.Progress = string.Empty; 88 | } 89 | 90 | project.Builds.Add(build); 91 | } 92 | } 93 | 94 | private dynamic GetJsonBuildTypeById(string id) 95 | { 96 | var count = (int)buildTypesJson.count; 97 | for (int i = 0; i < count; i++) 98 | { 99 | if (buildTypesJson.buildType[i].id == id) 100 | { 101 | return buildTypesJson.buildType[i]; 102 | } 103 | } 104 | 105 | return null; 106 | } 107 | 108 | private bool IsBuildQueued(string buildId) 109 | { 110 | try 111 | { 112 | var count = (int)buildQueueJson.count; 113 | for (int i = 0; i < count; i++) 114 | { 115 | var build = buildQueueJson.build[i]; 116 | 117 | if (buildId == (string)build.buildTypeId && (string)build.state == "queued") 118 | { 119 | return true; 120 | } 121 | } 122 | } 123 | catch 124 | { 125 | } 126 | 127 | return false; 128 | } 129 | 130 | private string GetUpdatedBy() 131 | { 132 | try 133 | { 134 | if ((string)buildStatusJson.triggered.type == "user") 135 | { 136 | return (string)buildStatusJson.triggered.user.name; 137 | } 138 | else if ((string)buildStatusJson.triggered.type == "unknown") 139 | { 140 | return "TeamCity"; 141 | } 142 | else 143 | { 144 | return "Unknown"; 145 | } 146 | } 147 | catch 148 | { 149 | return "Unknown"; 150 | } 151 | } 152 | } 153 | } -------------------------------------------------------------------------------- /BuildMonitor/Helpers/DefaultBuildMonitorModelHandler.cs: -------------------------------------------------------------------------------- 1 | using BuildMonitor.Models.Index; 2 | using Newtonsoft.Json; 3 | 4 | namespace BuildMonitor.Helpers 5 | { 6 | public class DefaultBuildMonitorModelHandler : BuildMonitorModelHandlerBase 7 | { 8 | public override BuildMonitorViewModel GetModel() 9 | { 10 | var model = new BuildMonitorViewModel(); 11 | 12 | GetTeamCityBuildsJson(); 13 | 14 | var count = (int)projectsJson.count; 15 | for (int i = 0; i < count; i++) 16 | { 17 | var project = new Project(); 18 | var projectJson = projectsJson.project[i]; 19 | 20 | project.Id = projectJson.id; 21 | project.Name = projectJson.name; 22 | AddBuilds(ref project); 23 | 24 | model.Projects.Add(project); 25 | } 26 | 27 | return model; 28 | } 29 | 30 | private void AddBuilds(ref Project project) 31 | { 32 | var count = (int)buildTypesJson.count; 33 | for (int i = 0; i < count; i++) 34 | { 35 | var buildTypeJson = buildTypesJson.buildType[i]; 36 | 37 | if (buildTypeJson.projectId != project.Id) 38 | { 39 | continue; 40 | } 41 | 42 | var build = new Build(); 43 | build.Id = buildTypeJson.id; 44 | build.Name = buildTypeJson.name; 45 | 46 | var url = string.Format(teamcitySettings.BuildStatus, build.Id); 47 | var buildStatusJsonString = RequestHelper.GetJson(url); 48 | buildStatusJson = JsonConvert.DeserializeObject(buildStatusJsonString ?? string.Empty); 49 | 50 | build.Branch = (buildStatusJson != null) ? (buildStatusJson.branchName ?? "default") : "unknown"; 51 | build.Status = GetBuildStatusForRunningBuild(build.Id); 52 | 53 | if (build.Status == BuildStatus.Running) 54 | { 55 | UpdateBuildStatusFromRunningBuildJson(build.Id); 56 | } 57 | 58 | build.UpdatedBy = GetUpdatedBy(); 59 | build.LastRunText = GetLastRunText(); 60 | build.IsQueued = IsBuildQueued(build.Id); 61 | build.StatusDescription = (string)buildStatusJson?.statusText ?? string.Empty; 62 | 63 | if (build.Status == BuildStatus.Running) 64 | { 65 | var result = GetRunningBuildBranchAndProgress(build.Id); 66 | build.Branch = result[0]; 67 | build.Progress = result[1]; 68 | } 69 | else 70 | { 71 | build.Progress = string.Empty; 72 | } 73 | 74 | project.Builds.Add(build); 75 | } 76 | } 77 | 78 | private bool IsBuildQueued(string buildId) 79 | { 80 | try 81 | { 82 | var count = (int)buildQueueJson.count; 83 | for (int i = 0; i < count; i++) 84 | { 85 | var build = buildQueueJson.build[i]; 86 | 87 | if (buildId == (string)build.buildTypeId && (string)build.state == "queued") 88 | { 89 | return true; 90 | } 91 | } 92 | } 93 | catch 94 | { 95 | } 96 | 97 | return false; 98 | } 99 | 100 | private string GetUpdatedBy() 101 | { 102 | try 103 | { 104 | var triggerType = (string)buildStatusJson.triggered.type; 105 | if (triggerType == "user") 106 | { 107 | return (string)buildStatusJson.triggered.user.name; 108 | } 109 | 110 | if (triggerType == "vcs" && buildStatusJson.lastChanges != null) 111 | { 112 | var result = RequestHelper.GetJson(teamcitySettings.Url + buildStatusJson.lastChanges.change[0].href); 113 | var change = JsonConvert.DeserializeObject(result); 114 | 115 | return (string)change.user.name; 116 | } 117 | 118 | if (triggerType == "unknown") 119 | { 120 | return "TeamCity"; 121 | } 122 | } 123 | catch 124 | { 125 | } 126 | 127 | return "Unknown"; 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /BuildMonitor/Helpers/IBuildMonitorModelHandler.cs: -------------------------------------------------------------------------------- 1 | using BuildMonitor.Models; 2 | using BuildMonitor.Models.Index; 3 | 4 | namespace BuildMonitor.Helpers 5 | { 6 | public interface IBuildMonitorModelHandler 7 | { 8 | void Initialize(TeamCitySettings settings); 9 | BuildMonitorViewModel GetModel(); 10 | } 11 | } -------------------------------------------------------------------------------- /BuildMonitor/Helpers/RequestHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | 5 | namespace BuildMonitor.Helpers 6 | { 7 | public static class RequestHelper 8 | { 9 | public static string Username { get; set; } 10 | public static string Password { get; set; } 11 | 12 | public static string GetJson(string url) 13 | { 14 | try 15 | { 16 | var request = (HttpWebRequest)WebRequest.Create(url); 17 | request.Accept = "application/json"; 18 | request.Headers.Add("ts", DateTime.Now.ToFileTime().ToString()); 19 | request.Credentials = new NetworkCredential(Username, Password); 20 | 21 | var response = request.GetResponse().GetResponseStream(); 22 | if (response == null) 23 | { 24 | return null; 25 | } 26 | 27 | var reader = new StreamReader(response); 28 | return reader.ReadToEnd(); 29 | } 30 | catch 31 | { 32 | return null; 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /BuildMonitor/Models/Index/Build.cs: -------------------------------------------------------------------------------- 1 | namespace BuildMonitor.Models.Index 2 | { 3 | public class Build 4 | { 5 | public string Id { get; set; } 6 | public string Name { get; set; } 7 | public BuildStatus Status { get; set; } 8 | public string Branch { get; set; } 9 | public string Progress { get; set; } 10 | public string UpdatedBy { get; set; } 11 | public string LastRunText { get; set; } 12 | public bool IsQueued { get; set; } 13 | public string StatusDescription { get; set; } 14 | 15 | 16 | public string StatusText 17 | { 18 | get 19 | { 20 | switch (Status) 21 | { 22 | case BuildStatus.Success: 23 | return "OK"; 24 | 25 | case BuildStatus.Failure: 26 | return "FAILED"; 27 | 28 | case BuildStatus.Running: 29 | return "RUNNING"; 30 | 31 | case BuildStatus.Error: 32 | return "ERROR"; 33 | 34 | default: 35 | return "UNKNOWN"; 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /BuildMonitor/Models/Index/BuildJson.cs: -------------------------------------------------------------------------------- 1 | namespace BuildMonitor.Models.Index 2 | { 3 | public class BuildJson 4 | { 5 | public string Id { get; set; } 6 | public string Content { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /BuildMonitor/Models/Index/BuildMonitorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace BuildMonitor.Models.Index 4 | { 5 | public class BuildMonitorViewModel 6 | { 7 | public List Projects { get; set; } 8 | 9 | public BuildMonitorViewModel() 10 | { 11 | Projects = new List(); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /BuildMonitor/Models/Index/BuildStatus.cs: -------------------------------------------------------------------------------- 1 | namespace BuildMonitor.Models.Index 2 | { 3 | public enum BuildStatus 4 | { 5 | None, 6 | Success, 7 | Failure, 8 | Error, 9 | Running 10 | } 11 | } -------------------------------------------------------------------------------- /BuildMonitor/Models/Index/BuildsJson.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace BuildMonitor.Models.Index 5 | { 6 | public class BuildsJson 7 | { 8 | public string UpdatedText { get; set; } 9 | public List Builds { get; set; } 10 | 11 | public BuildsJson() 12 | { 13 | UpdatedText = "Updated " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); 14 | Builds = new List(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /BuildMonitor/Models/Index/HiddenJob.cs: -------------------------------------------------------------------------------- 1 | namespace BuildMonitor.Models.Index 2 | { 3 | public class HiddenJob 4 | { 5 | public string Project { get; set; } 6 | public string Job { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /BuildMonitor/Models/Index/Project.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace BuildMonitor.Models.Index 4 | { 5 | public class Project 6 | { 7 | public string Id { get; set; } 8 | public string Name { get; set; } 9 | public List Builds { get; set; } 10 | 11 | public Project() 12 | { 13 | Builds = new List(); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /BuildMonitor/Models/Index/Settings/Group.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Xml.Serialization; 3 | 4 | namespace BuildMonitor.Models.Index.Settings 5 | { 6 | public class Group 7 | { 8 | [XmlAttribute("name")] 9 | public string Name { get; set; } 10 | 11 | public List Jobs { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /BuildMonitor/Models/Index/Settings/Job.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Serialization; 2 | 3 | namespace BuildMonitor.Models.Index.Settings 4 | { 5 | public class Job 6 | { 7 | [XmlAttribute("id")] 8 | public string Id { get; set; } 9 | 10 | [XmlAttribute("text")] 11 | public string Text { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /BuildMonitor/Models/Index/Settings/Settings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace BuildMonitor.Models.Index.Settings 4 | { 5 | public class Settings 6 | { 7 | public List Groups { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /BuildMonitor/Models/SecretTeamCitySettings.cs: -------------------------------------------------------------------------------- 1 | namespace BuildMonitor.Models 2 | { 3 | /// 4 | /// Safe storage of app secrets in development in ASP.NET Core 5 | /// https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-2.2&tabs=windows 6 | /// 7 | public class SecretTeamCitySettings 8 | { 9 | public string UserName { get; set; } 10 | public string Password { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /BuildMonitor/Models/TeamCitySettings.cs: -------------------------------------------------------------------------------- 1 | namespace BuildMonitor.Models 2 | { 3 | /// 4 | /// These are loaded from appsettings.json. Note that UserName and Password can be overridden from a user secrets file. 5 | /// See SecretTeamCitySettings.cs for more information of how to use encrypted/hidden credentials. 6 | /// 7 | public class TeamCitySettings 8 | { 9 | public string UserName { get; set; } 10 | public string Password { get; set; } 11 | public string Url { get; set; } 12 | public string Projects { get; set; } 13 | public string BuildTypes { get; set; } 14 | public string BuildStatus { get; set; } 15 | public string RunningBuilds { get; set; } 16 | public string BuildQueue { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /BuildMonitor/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

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

13 | Request ID: @Model.RequestId 14 |

15 | } 16 | 17 |

Development Mode

18 |

19 | Swapping to the Development environment displays detailed information about the error that occurred. 20 |

21 |

22 | The Development environment shouldn't be enabled for deployed applications. 23 | It can result in displaying sensitive information from exceptions to end users. 24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 25 | and restarting the app. 26 |

27 | -------------------------------------------------------------------------------- /BuildMonitor/Pages/Error.cshtml.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 Microsoft.AspNetCore.Mvc.RazorPages; 8 | 9 | namespace BuildMonitor.Pages 10 | { 11 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 12 | public class ErrorModel : PageModel 13 | { 14 | public string RequestId { get; set; } 15 | 16 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 17 | 18 | public void OnGet() 19 | { 20 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /BuildMonitor/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model IndexModel 3 | @{ 4 | ViewData["Title"] = "Build Monitor"; 5 | } 6 | 7 | @foreach (var project in Model.Projects) 8 | { 9 | if (project.Builds.Count == 0) 10 | { 11 | continue; 12 | } 13 | 14 |
15 |

▪▪▪ @project.Name ▪▪▪

16 |
17 | @foreach (var build in project.Builds) 18 | { 19 | @Html.Partial("_BuildItem", build); 20 | } 21 |
22 |
23 | } -------------------------------------------------------------------------------- /BuildMonitor/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using BuildMonitor.Helpers; 3 | using BuildMonitor.Models; 4 | using BuildMonitor.Models.Index; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | using Microsoft.Extensions.Options; 7 | 8 | namespace BuildMonitor.Pages 9 | { 10 | public class IndexModel : PageModel 11 | { 12 | public List Projects { get; set; } 13 | 14 | private readonly IBuildMonitorModelHandler modelHandler; 15 | 16 | public IndexModel(IOptions config) 17 | { 18 | RequestHelper.Username = config.Value.UserName; 19 | RequestHelper.Password = config.Value.Password; 20 | 21 | modelHandler = new DefaultBuildMonitorModelHandler(); 22 | //modelHandler = new CustomBuildMonitorModelHandler(); 23 | modelHandler.Initialize(config.Value); 24 | 25 | var model = modelHandler.GetModel(); 26 | Projects = model.Projects; 27 | } 28 | 29 | public void OnGet() 30 | { 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /BuildMonitor/Pages/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model PrivacyModel 3 | @{ 4 | ViewData["Title"] = "Privacy Policy"; 5 | } 6 |

@ViewData["Title"]

7 | 8 |

Use this page to detail your site's privacy policy.

9 | -------------------------------------------------------------------------------- /BuildMonitor/Pages/Privacy.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace BuildMonitor.Pages 9 | { 10 | public class PrivacyModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /BuildMonitor/Pages/Shared/_CookieConsentPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Http.Features 2 | 3 | @{ 4 | var consentFeature = Context.Features.Get(); 5 | var showBanner = !consentFeature?.CanTrack ?? false; 6 | var cookieString = consentFeature?.CreateConsentCookie(); 7 | } 8 | 9 | @if (showBanner) 10 | { 11 | 17 | 25 | } 26 | -------------------------------------------------------------------------------- /BuildMonitor/Pages/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewData["Title"] 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | @RenderBody() 15 |
16 |
17 |
18 |
19 | TeamCity BuildMonitor 20 | Updated @DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") 21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 36 | 42 | 43 | 44 | 45 | @RenderSection("Scripts", required: false) 46 | 47 | 48 | -------------------------------------------------------------------------------- /BuildMonitor/Pages/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /BuildMonitor/Pages/_BuildItem.cshtml: -------------------------------------------------------------------------------- 1 | @model BuildMonitor.Models.Index.Build 2 | 3 |
4 |
5 |

@Model.Name

6 |

@Model.StatusText

7 |

@Model.StatusDescription

8 |

9 | Branch @Model.Branch 10 | @if (!string.IsNullOrWhiteSpace(Model.Progress)) 11 | { 12 | ▪ job @Model.Progress done 13 | } 14 | else if (Model.IsQueued) 15 | { 16 | queued 17 | } 18 |

19 |

Last updated by @Model.UpdatedBy @Model.LastRunText

20 |
21 |
-------------------------------------------------------------------------------- /BuildMonitor/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using BuildMonitor 2 | @namespace BuildMonitor.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /BuildMonitor/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /BuildMonitor/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore; 2 | using Microsoft.AspNetCore.Hosting; 3 | 4 | namespace BuildMonitor 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateWebHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 14 | WebHost.CreateDefaultBuilder(args) 15 | .UseStartup(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /BuildMonitor/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:62023", 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 | "BuildMonitor": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /BuildMonitor/Startup.cs: -------------------------------------------------------------------------------- 1 | using BuildMonitor.Models; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | 9 | namespace BuildMonitor 10 | { 11 | public class Startup 12 | { 13 | public Startup(IConfiguration configuration) 14 | { 15 | Configuration = configuration; 16 | } 17 | 18 | public IConfiguration Configuration { get; } 19 | 20 | // This method gets called by the runtime. Use this method to add services to the container. 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.Configure(options => 24 | { 25 | // This lambda determines whether user consent for non-essential cookies is needed for a given request. 26 | options.CheckConsentNeeded = context => true; 27 | options.MinimumSameSitePolicy = SameSiteMode.None; 28 | }); 29 | 30 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); 31 | 32 | services.Configure(Configuration.GetSection("TeamCity")); 33 | } 34 | 35 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 36 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 37 | { 38 | if (env.IsDevelopment()) 39 | { 40 | app.UseDeveloperExceptionPage(); 41 | } 42 | else 43 | { 44 | app.UseExceptionHandler("/Error"); 45 | } 46 | 47 | app.UseStaticFiles(); 48 | app.UseCookiePolicy(); 49 | 50 | app.UseMvc(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /BuildMonitor/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /BuildMonitor/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "TeamCity": { 3 | "UserName": "", 4 | "Password": "", 5 | "Url": "", 6 | "Projects": "/httpAuth/app/rest/projects", 7 | "BuildTypes": "/httpAuth/app/rest/buildTypes", 8 | "BuildStatus": "/httpAuth/app/rest/buildTypes/id:{0}/builds/branch:(default:any)", 9 | "RunningBuilds": "/httpAuth/app/rest/builds?locator=running:true,branch:(default:any)", 10 | "BuildQueue": "/httpAuth/app/rest/buildQueue" 11 | }, 12 | "Logging": { 13 | "LogLevel": { 14 | "Default": "Warning" 15 | } 16 | }, 17 | "AllowedHosts": "*" 18 | } -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | .container-full { 2 | margin: 0 auto; 3 | width: 100%; 4 | padding-left: 25px; 5 | padding-right: 25px; 6 | } 7 | 8 | body { 9 | background-color: #272B30; 10 | padding-top: 20px; 11 | padding-bottom: 20px; 12 | font-family: kenzo; 13 | text-align: left; 14 | } 15 | 16 | a { 17 | cursor: pointer; 18 | } 19 | 20 | @font-face { 21 | font-family: 'kenzo'; 22 | src: url('../fonts/kenzo.eot'); 23 | src: url('../fonts/kenzo.eot?#iefix') format('embedded-opentype'), url('../fonts/kenzo.woff') format('woff'), url('../fonts/kenzo.ttf') format('truetype'); 24 | } 25 | 26 | /* Set padding to keep content from hitting the edges */ 27 | .body-content { 28 | padding-left: 15px; 29 | padding-right: 15px; 30 | } 31 | 32 | /* Override the default bootstrap behavior where horizontal description lists 33 | will truncate terms that are too long to fit in the left column 34 | */ 35 | .dl-horizontal dt { 36 | white-space: normal; 37 | } 38 | 39 | /* Set width on the form input elements since they're 100% wide by default */ 40 | input, 41 | select, 42 | textarea { 43 | max-width: 280px; 44 | } 45 | 46 | /* Custom */ 47 | 48 | html * { 49 | font-family: kenzo !important; 50 | } 51 | 52 | .navbar-space { 53 | padding-left: 20px !important; 54 | padding-right: 20px !important; 55 | } 56 | 57 | /* Build monitor --------------------------------------------------------------------------------- */ 58 | 59 | .jumbotron { 60 | padding: 1rem 1rem; 61 | } 62 | 63 | .projectRow { 64 | clear: both; 65 | } 66 | 67 | .projectTitle { 68 | color: #777; 69 | margin-top: -12px; 70 | display: inline-block; 71 | } 72 | 73 | .buildTitle { 74 | font-size: 30px; 75 | color: #eee; 76 | margin-top: -5px; 77 | } 78 | 79 | .buildInfo { 80 | margin-top: -8px; 81 | margin-bottom: -3px; 82 | color: #777; 83 | } 84 | 85 | .buildInfoHighlight { 86 | color: #999; 87 | } 88 | 89 | .buildInfoBranch { 90 | font-size: 20px; 91 | color: #777; 92 | margin-bottom: 2px; 93 | } 94 | 95 | .buildInfoProgressHighlight { 96 | -webkit-animation-name: pulsate; /* Chrome, Safari, Opera */ 97 | -webkit-animation-duration: 1s; /* Chrome, Safari, Opera */ 98 | -webkit-animation-iteration-count: infinite; /* Chrome, Safari, Opera */ 99 | animation-name: pulsate; 100 | animation-duration: 1s; 101 | animation-iteration-count: infinite; 102 | } 103 | 104 | .text-cutoff { 105 | white-space: nowrap; 106 | overflow: hidden; 107 | text-overflow: ellipsis; 108 | } 109 | 110 | .status-None { 111 | font-family: kenzo; 112 | font-size: 75px; 113 | color: #BBB; 114 | margin-top: -20px; 115 | margin-bottom: -5px; 116 | } 117 | 118 | .status-Success { 119 | font-family: kenzo; 120 | font-size: 75px; 121 | color: #A9E800; 122 | margin-top: -20px; 123 | margin-bottom: -5px; 124 | } 125 | 126 | .status-Failure { 127 | font-family: kenzo; 128 | font-size: 75px; 129 | color: #FB1B45; 130 | margin-top: -20px; 131 | margin-bottom: -5px; 132 | } 133 | 134 | .status-Error { 135 | font-family: kenzo; 136 | font-size: 75px; 137 | color: #FB1B45; 138 | margin-top: -20px; 139 | margin-bottom: -5px; 140 | } 141 | 142 | .status-Running { 143 | font-family: kenzo; 144 | font-size: 75px; 145 | margin-top: -20px; 146 | margin-bottom: -5px; 147 | -webkit-animation-name: pulsate; /* Chrome, Safari, Opera */ 148 | -webkit-animation-duration: 1s; /* Chrome, Safari, Opera */ 149 | -webkit-animation-iteration-count: infinite; /* Chrome, Safari, Opera */ 150 | animation-name: pulsate; 151 | animation-duration: 1s; 152 | animation-iteration-count: infinite; 153 | } 154 | 155 | .loader-charts { 156 | font-family: kenzo; 157 | font-size: 14px; 158 | margin-top: -15px; 159 | -webkit-animation-name: pulsate; /* Chrome, Safari, Opera */ 160 | -webkit-animation-duration: 1s; /* Chrome, Safari, Opera */ 161 | -webkit-animation-iteration-count: infinite; /* Chrome, Safari, Opera */ 162 | animation-name: pulsate; 163 | animation-duration: 1s; 164 | animation-iteration-count: infinite; 165 | } 166 | 167 | /* Chrome, Safari, Opera */ 168 | @-webkit-keyframes pulsate { 169 | 0% { 170 | color: #CFC291; 171 | } 172 | 173 | 50% { 174 | color: #FFF6C5; 175 | } 176 | 177 | 100% { 178 | color: #CFC291; 179 | } 180 | } 181 | 182 | /* Color animation */ 183 | @keyframes pulsate { 184 | 0% { 185 | color: #CFC291; 186 | } 187 | 188 | 50% { 189 | color: #FFF6C5; 190 | } 191 | 192 | 100% { 193 | color: #CFC291; 194 | } 195 | } 196 | 197 | /* Chrome, Safari, Opera */ 198 | @-webkit-keyframes pulsate2 { 199 | 0% { 200 | color: #FB1B45; 201 | } 202 | 203 | 50% { 204 | color: #ff8686; 205 | } 206 | 207 | 100% { 208 | color: #FB1B45; 209 | } 210 | } 211 | 212 | /* Color animation */ 213 | @keyframes pulsate2 { 214 | 0% { 215 | color: #FB1B45; 216 | } 217 | 218 | 50% { 219 | color: #ff8686; 220 | } 221 | 222 | 100% { 223 | color: #FB1B45; 224 | } 225 | } 226 | 227 | .span-chart { 228 | color: #777; 229 | } 230 | 231 | .space { 232 | padding-left: 20px; 233 | } 234 | -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohanGl/TeamCity_BuildMonitor/1d83847444fe0ffdea576d5bccde0958861b34e0/BuildMonitor/wwwroot/favicon.ico -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohanGl/TeamCity_BuildMonitor/1d83847444fe0ffdea576d5bccde0958861b34e0/BuildMonitor/wwwroot/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohanGl/TeamCity_BuildMonitor/1d83847444fe0ffdea576d5bccde0958861b34e0/BuildMonitor/wwwroot/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohanGl/TeamCity_BuildMonitor/1d83847444fe0ffdea576d5bccde0958861b34e0/BuildMonitor/wwwroot/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/fonts/kenzo.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohanGl/TeamCity_BuildMonitor/1d83847444fe0ffdea576d5bccde0958861b34e0/BuildMonitor/wwwroot/fonts/kenzo.eot -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/fonts/kenzo.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohanGl/TeamCity_BuildMonitor/1d83847444fe0ffdea576d5bccde0958861b34e0/BuildMonitor/wwwroot/fonts/kenzo.otf -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/fonts/kenzo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohanGl/TeamCity_BuildMonitor/1d83847444fe0ffdea576d5bccde0958861b34e0/BuildMonitor/wwwroot/fonts/kenzo.ttf -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/fonts/kenzo.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohanGl/TeamCity_BuildMonitor/1d83847444fe0ffdea576d5bccde0958861b34e0/BuildMonitor/wwwroot/fonts/kenzo.woff -------------------------------------------------------------------------------- /BuildMonitor/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 | $(document).ready(function () { 5 | $('body').css('display', 'none'); 6 | $('body').fadeIn(1000); 7 | 8 | setInterval(function () { 9 | $.ajax({ 10 | url: '/Home/GetBuilds', 11 | success: function (data) { 12 | $.each(data.Builds, function (i, build) { 13 | var divId = "#BuildDiv-" + build.Id; 14 | var buildDiv = $(divId); 15 | buildDiv.replaceWith(build.Content); 16 | }); 17 | 18 | $("#last-updated").text(data.UpdatedText); 19 | }, 20 | cache: false 21 | }); 22 | }, 15000); 23 | }); -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2018 Twitter, Inc. 4 | Copyright (c) 2011-2018 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Grid v4.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2018 The Bootstrap Authors 4 | * Copyright 2011-2018 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | */@-ms-viewport{width:device-width}html{box-sizing:border-box;-ms-overflow-style:scrollbar}*,::after,::before{box-sizing:inherit}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}} 7 | /*# sourceMappingURL=bootstrap-grid.min.css.map */ -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2018 The Bootstrap Authors 4 | * Copyright 2011-2018 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | *, 9 | *::before, 10 | *::after { 11 | box-sizing: border-box; 12 | } 13 | 14 | html { 15 | font-family: sans-serif; 16 | line-height: 1.15; 17 | -webkit-text-size-adjust: 100%; 18 | -ms-text-size-adjust: 100%; 19 | -ms-overflow-style: scrollbar; 20 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 21 | } 22 | 23 | @-ms-viewport { 24 | width: device-width; 25 | } 26 | 27 | article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { 28 | display: block; 29 | } 30 | 31 | body { 32 | margin: 0; 33 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 34 | font-size: 1rem; 35 | font-weight: 400; 36 | line-height: 1.5; 37 | color: #212529; 38 | text-align: left; 39 | background-color: #fff; 40 | } 41 | 42 | [tabindex="-1"]:focus { 43 | outline: 0 !important; 44 | } 45 | 46 | hr { 47 | box-sizing: content-box; 48 | height: 0; 49 | overflow: visible; 50 | } 51 | 52 | h1, h2, h3, h4, h5, h6 { 53 | margin-top: 0; 54 | margin-bottom: 0.5rem; 55 | } 56 | 57 | p { 58 | margin-top: 0; 59 | margin-bottom: 1rem; 60 | } 61 | 62 | abbr[title], 63 | abbr[data-original-title] { 64 | text-decoration: underline; 65 | -webkit-text-decoration: underline dotted; 66 | text-decoration: underline dotted; 67 | cursor: help; 68 | border-bottom: 0; 69 | } 70 | 71 | address { 72 | margin-bottom: 1rem; 73 | font-style: normal; 74 | line-height: inherit; 75 | } 76 | 77 | ol, 78 | ul, 79 | dl { 80 | margin-top: 0; 81 | margin-bottom: 1rem; 82 | } 83 | 84 | ol ol, 85 | ul ul, 86 | ol ul, 87 | ul ol { 88 | margin-bottom: 0; 89 | } 90 | 91 | dt { 92 | font-weight: 700; 93 | } 94 | 95 | dd { 96 | margin-bottom: .5rem; 97 | margin-left: 0; 98 | } 99 | 100 | blockquote { 101 | margin: 0 0 1rem; 102 | } 103 | 104 | dfn { 105 | font-style: italic; 106 | } 107 | 108 | b, 109 | strong { 110 | font-weight: bolder; 111 | } 112 | 113 | small { 114 | font-size: 80%; 115 | } 116 | 117 | sub, 118 | sup { 119 | position: relative; 120 | font-size: 75%; 121 | line-height: 0; 122 | vertical-align: baseline; 123 | } 124 | 125 | sub { 126 | bottom: -.25em; 127 | } 128 | 129 | sup { 130 | top: -.5em; 131 | } 132 | 133 | a { 134 | color: #007bff; 135 | text-decoration: none; 136 | background-color: transparent; 137 | -webkit-text-decoration-skip: objects; 138 | } 139 | 140 | a:hover { 141 | color: #0056b3; 142 | text-decoration: underline; 143 | } 144 | 145 | a:not([href]):not([tabindex]) { 146 | color: inherit; 147 | text-decoration: none; 148 | } 149 | 150 | a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { 151 | color: inherit; 152 | text-decoration: none; 153 | } 154 | 155 | a:not([href]):not([tabindex]):focus { 156 | outline: 0; 157 | } 158 | 159 | pre, 160 | code, 161 | kbd, 162 | samp { 163 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 164 | font-size: 1em; 165 | } 166 | 167 | pre { 168 | margin-top: 0; 169 | margin-bottom: 1rem; 170 | overflow: auto; 171 | -ms-overflow-style: scrollbar; 172 | } 173 | 174 | figure { 175 | margin: 0 0 1rem; 176 | } 177 | 178 | img { 179 | vertical-align: middle; 180 | border-style: none; 181 | } 182 | 183 | svg { 184 | overflow: hidden; 185 | vertical-align: middle; 186 | } 187 | 188 | table { 189 | border-collapse: collapse; 190 | } 191 | 192 | caption { 193 | padding-top: 0.75rem; 194 | padding-bottom: 0.75rem; 195 | color: #6c757d; 196 | text-align: left; 197 | caption-side: bottom; 198 | } 199 | 200 | th { 201 | text-align: inherit; 202 | } 203 | 204 | label { 205 | display: inline-block; 206 | margin-bottom: 0.5rem; 207 | } 208 | 209 | button { 210 | border-radius: 0; 211 | } 212 | 213 | button:focus { 214 | outline: 1px dotted; 215 | outline: 5px auto -webkit-focus-ring-color; 216 | } 217 | 218 | input, 219 | button, 220 | select, 221 | optgroup, 222 | textarea { 223 | margin: 0; 224 | font-family: inherit; 225 | font-size: inherit; 226 | line-height: inherit; 227 | } 228 | 229 | button, 230 | input { 231 | overflow: visible; 232 | } 233 | 234 | button, 235 | select { 236 | text-transform: none; 237 | } 238 | 239 | button, 240 | html [type="button"], 241 | [type="reset"], 242 | [type="submit"] { 243 | -webkit-appearance: button; 244 | } 245 | 246 | button::-moz-focus-inner, 247 | [type="button"]::-moz-focus-inner, 248 | [type="reset"]::-moz-focus-inner, 249 | [type="submit"]::-moz-focus-inner { 250 | padding: 0; 251 | border-style: none; 252 | } 253 | 254 | input[type="radio"], 255 | input[type="checkbox"] { 256 | box-sizing: border-box; 257 | padding: 0; 258 | } 259 | 260 | input[type="date"], 261 | input[type="time"], 262 | input[type="datetime-local"], 263 | input[type="month"] { 264 | -webkit-appearance: listbox; 265 | } 266 | 267 | textarea { 268 | overflow: auto; 269 | resize: vertical; 270 | } 271 | 272 | fieldset { 273 | min-width: 0; 274 | padding: 0; 275 | margin: 0; 276 | border: 0; 277 | } 278 | 279 | legend { 280 | display: block; 281 | width: 100%; 282 | max-width: 100%; 283 | padding: 0; 284 | margin-bottom: .5rem; 285 | font-size: 1.5rem; 286 | line-height: inherit; 287 | color: inherit; 288 | white-space: normal; 289 | } 290 | 291 | progress { 292 | vertical-align: baseline; 293 | } 294 | 295 | [type="number"]::-webkit-inner-spin-button, 296 | [type="number"]::-webkit-outer-spin-button { 297 | height: auto; 298 | } 299 | 300 | [type="search"] { 301 | outline-offset: -2px; 302 | -webkit-appearance: none; 303 | } 304 | 305 | [type="search"]::-webkit-search-cancel-button, 306 | [type="search"]::-webkit-search-decoration { 307 | -webkit-appearance: none; 308 | } 309 | 310 | ::-webkit-file-upload-button { 311 | font: inherit; 312 | -webkit-appearance: button; 313 | } 314 | 315 | output { 316 | display: inline-block; 317 | } 318 | 319 | summary { 320 | display: list-item; 321 | cursor: pointer; 322 | } 323 | 324 | template { 325 | display: none; 326 | } 327 | 328 | [hidden] { 329 | display: none !important; 330 | } 331 | /*# sourceMappingURL=bootstrap-reboot.css.map */ -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2018 The Bootstrap Authors 4 | * Copyright 2011-2018 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../scss/bootstrap-reboot.scss","../../scss/_reboot.scss","dist/css/bootstrap-reboot.css","bootstrap-reboot.css","../../scss/mixins/_hover.scss"],"names":[],"mappings":"AAAA;;;;;;ACoBA,ECXA,QADA,SDeE,WAAA,WAGF,KACE,YAAA,WACA,YAAA,KACA,yBAAA,KACA,qBAAA,KACA,mBAAA,UACA,4BAAA,YAKA,cACE,MAAA,aAMJ,QAAA,MAAA,WAAA,OAAA,OAAA,OAAA,OAAA,KAAA,IAAA,QACE,QAAA,MAWF,KACE,OAAA,EACA,YAAA,aAAA,CAAA,kBAAA,CAAA,UAAA,CAAA,MAAA,CAAA,gBAAA,CAAA,KAAA,CAAA,UAAA,CAAA,mBAAA,CAAA,gBAAA,CAAA,iBAAA,CAAA,mBACA,UAAA,KACA,YAAA,IACA,YAAA,IACA,MAAA,QACA,WAAA,KACA,iBAAA,KEvBF,sBFgCE,QAAA,YASF,GACE,WAAA,YACA,OAAA,EACA,SAAA,QAaF,GAAA,GAAA,GAAA,GAAA,GAAA,GACE,WAAA,EACA,cAAA,MAQF,EACE,WAAA,EACA,cAAA,KChDF,0BD0DA,YAEE,gBAAA,UACA,wBAAA,UAAA,OAAA,gBAAA,UAAA,OACA,OAAA,KACA,cAAA,EAGF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QCrDF,GDwDA,GCzDA,GD4DE,WAAA,EACA,cAAA,KAGF,MCxDA,MACA,MAFA,MD6DE,cAAA,EAGF,GACE,YAAA,IAGF,GACE,cAAA,MACA,YAAA,EAGF,WACE,OAAA,EAAA,EAAA,KAGF,IACE,WAAA,OAIF,EC1DA,OD4DE,YAAA,OAIF,MACE,UAAA,IAQF,IChEA,IDkEE,SAAA,SACA,UAAA,IACA,YAAA,EACA,eAAA,SAGF,IAAM,OAAA,OACN,IAAM,IAAA,MAON,EACE,MAAA,QACA,gBAAA,KACA,iBAAA,YACA,6BAAA,QG7LA,QHgME,MAAA,QACA,gBAAA,UAUJ,8BACE,MAAA,QACA,gBAAA,KGzMA,oCAAA,oCH4ME,MAAA,QACA,gBAAA,KANJ,oCAUI,QAAA,EClEJ,KACA,ID0EA,ICzEA,KD6EE,YAAA,cAAA,CAAA,KAAA,CAAA,MAAA,CAAA,QAAA,CAAA,iBAAA,CAAA,aAAA,CAAA,UACA,UAAA,IAGF,IAEE,WAAA,EAEA,cAAA,KAEA,SAAA,KAGA,mBAAA,UAQF,OAEE,OAAA,EAAA,EAAA,KAQF,IACE,eAAA,OACA,aAAA,KAGF,IAGE,SAAA,OACA,eAAA,OAQF,MACE,gBAAA,SAGF,QACE,YAAA,OACA,eAAA,OACA,MAAA,QACA,WAAA,KACA,aAAA,OAGF,GAGE,WAAA,QAQF,MAEE,QAAA,aACA,cAAA,MAMF,OACE,cAAA,EAOF,aACE,QAAA,IAAA,OACA,QAAA,IAAA,KAAA,yBC9GF,ODiHA,MC/GA,SADA,OAEA,SDmHE,OAAA,EACA,YAAA,QACA,UAAA,QACA,YAAA,QAGF,OCjHA,MDmHE,SAAA,QAGF,OCjHA,ODmHE,eAAA,KC7GF,aACA,cDkHA,OCpHA,mBDwHE,mBAAA,OCjHF,gCACA,+BACA,gCDmHA,yBAIE,QAAA,EACA,aAAA,KClHF,qBDqHA,kBAEE,WAAA,WACA,QAAA,EAIF,iBCrHA,2BACA,kBAFA,iBD+HE,mBAAA,QAGF,SACE,SAAA,KAEA,OAAA,SAGF,SAME,UAAA,EAEA,QAAA,EACA,OAAA,EACA,OAAA,EAKF,OACE,QAAA,MACA,MAAA,KACA,UAAA,KACA,QAAA,EACA,cAAA,MACA,UAAA,OACA,YAAA,QACA,MAAA,QACA,YAAA,OAGF,SACE,eAAA,SEnIF,yCDEA,yCDuIE,OAAA,KEpIF,cF4IE,eAAA,KACA,mBAAA,KExIF,4CDEA,yCD+IE,mBAAA,KAQF,6BACE,KAAA,QACA,mBAAA,OAOF,OACE,QAAA,aAGF,QACE,QAAA,UACA,OAAA,QAGF,SACE,QAAA,KErJF,SF2JE,QAAA","sourcesContent":["/*!\n * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"reboot\";\n","// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n// 2. Change the default font family in all browsers.\n// 3. Correct the line height in all browsers.\n// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.\n// 5. Setting @viewport causes scrollbars to overlap content in IE11 and Edge, so\n// we force a non-overlapping, non-auto-hiding scrollbar to counteract.\n// 6. Change the default tap highlight to be completely transparent in iOS.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box; // 1\n}\n\nhtml {\n font-family: sans-serif; // 2\n line-height: 1.15; // 3\n -webkit-text-size-adjust: 100%; // 4\n -ms-text-size-adjust: 100%; // 4\n -ms-overflow-style: scrollbar; // 5\n -webkit-tap-highlight-color: rgba($black, 0); // 6\n}\n\n// IE10+ doesn't honor `` in some cases.\n@at-root {\n @-ms-viewport {\n width: device-width;\n }\n}\n\n// stylelint-disable selector-list-comma-newline-after\n// Shim for \"new\" HTML5 structural elements to display correctly (IE10, older browsers)\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n// stylelint-enable selector-list-comma-newline-after\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Set an explicit initial text-align value so that we can later use the\n// the `inherit` value on things like `` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n font-size: $font-size-base;\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Suppress the focus outline on elements that cannot be accessed via keyboard.\n// This prevents an unwanted focus outline from appearing around elements that\n// might still respond to pointer events.\n//\n// Credit: https://github.com/suitcss/base\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n// stylelint-enable selector-list-comma-newline-after\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Remove the bottom border in Firefox 39-.\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Duplicate behavior to the data-* attribute for our tooltip plugin\n\nabbr[title],\nabbr[data-original-title] { // 4\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 1\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic; // Add the correct font style in Android 4.3-\n}\n\n// stylelint-disable font-weight-notation\nb,\nstrong {\n font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n// stylelint-enable font-weight-notation\n\nsmall {\n font-size: 80%; // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n -webkit-text-decoration-skip: objects; // Remove gaps in links underline in iOS 8+ and Safari 8+.\n\n @include hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href)\n// which have not been made explicitly keyboard-focusable (without tabindex).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n\n @include hover-focus {\n color: inherit;\n text-decoration: none;\n }\n\n &:focus {\n outline: 0;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n font-size: 1em; // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n // We have @viewport set which causes scrollbars to overlap content in IE11 and Edge, so\n // we force a non-overlapping, non-auto-hiding scrollbar to counteract.\n -ms-overflow-style: scrollbar;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg {\n // Workaround for the SVG overflow bug in IE10/11 is still required.\n // See https://github.com/twbs/bootstrap/issues/26878\n overflow: hidden;\n vertical-align: middle;\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\nhtml [type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `

`s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding and cancel buttons in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","/*!\n * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n -ms-overflow-style: scrollbar;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\n@-ms-viewport {\n width: device-width;\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n -ms-overflow-style: scrollbar;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n/*# sourceMappingURL=bootstrap-reboot.css.map */","/*!\n * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n -ms-overflow-style: scrollbar;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\n@-ms-viewport {\n width: device-width;\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n -ms-overflow-style: scrollbar;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\n/*# sourceMappingURL=bootstrap-reboot.css.map */","// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Originally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS-an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular pseudo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover {\n &:hover { @content; }\n}\n\n@mixin hover-focus {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n"]} -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js: -------------------------------------------------------------------------------- 1 | // Unobtrusive validation support library for jQuery and jQuery Validate 2 | // Copyright (c) .NET Foundation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 4 | // @version v3.2.11 5 | 6 | /*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */ 7 | /*global document: false, jQuery: false */ 8 | 9 | (function (factory) { 10 | if (typeof define === 'function' && define.amd) { 11 | // AMD. Register as an anonymous module. 12 | define("jquery.validate.unobtrusive", ['jquery-validation'], factory); 13 | } else if (typeof module === 'object' && module.exports) { 14 | // CommonJS-like environments that support module.exports 15 | module.exports = factory(require('jquery-validation')); 16 | } else { 17 | // Browser global 18 | jQuery.validator.unobtrusive = factory(jQuery); 19 | } 20 | }(function ($) { 21 | var $jQval = $.validator, 22 | adapters, 23 | data_validation = "unobtrusiveValidation"; 24 | 25 | function setValidationValues(options, ruleName, value) { 26 | options.rules[ruleName] = value; 27 | if (options.message) { 28 | options.messages[ruleName] = options.message; 29 | } 30 | } 31 | 32 | function splitAndTrim(value) { 33 | return value.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/g); 34 | } 35 | 36 | function escapeAttributeValue(value) { 37 | // As mentioned on http://api.jquery.com/category/selectors/ 38 | return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1"); 39 | } 40 | 41 | function getModelPrefix(fieldName) { 42 | return fieldName.substr(0, fieldName.lastIndexOf(".") + 1); 43 | } 44 | 45 | function appendModelPrefix(value, prefix) { 46 | if (value.indexOf("*.") === 0) { 47 | value = value.replace("*.", prefix); 48 | } 49 | return value; 50 | } 51 | 52 | function onError(error, inputElement) { // 'this' is the form element 53 | var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"), 54 | replaceAttrValue = container.attr("data-valmsg-replace"), 55 | replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null; 56 | 57 | container.removeClass("field-validation-valid").addClass("field-validation-error"); 58 | error.data("unobtrusiveContainer", container); 59 | 60 | if (replace) { 61 | container.empty(); 62 | error.removeClass("input-validation-error").appendTo(container); 63 | } 64 | else { 65 | error.hide(); 66 | } 67 | } 68 | 69 | function onErrors(event, validator) { // 'this' is the form element 70 | var container = $(this).find("[data-valmsg-summary=true]"), 71 | list = container.find("ul"); 72 | 73 | if (list && list.length && validator.errorList.length) { 74 | list.empty(); 75 | container.addClass("validation-summary-errors").removeClass("validation-summary-valid"); 76 | 77 | $.each(validator.errorList, function () { 78 | $("
  • ").html(this.message).appendTo(list); 79 | }); 80 | } 81 | } 82 | 83 | function onSuccess(error) { // 'this' is the form element 84 | var container = error.data("unobtrusiveContainer"); 85 | 86 | if (container) { 87 | var replaceAttrValue = container.attr("data-valmsg-replace"), 88 | replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null; 89 | 90 | container.addClass("field-validation-valid").removeClass("field-validation-error"); 91 | error.removeData("unobtrusiveContainer"); 92 | 93 | if (replace) { 94 | container.empty(); 95 | } 96 | } 97 | } 98 | 99 | function onReset(event) { // 'this' is the form element 100 | var $form = $(this), 101 | key = '__jquery_unobtrusive_validation_form_reset'; 102 | if ($form.data(key)) { 103 | return; 104 | } 105 | // Set a flag that indicates we're currently resetting the form. 106 | $form.data(key, true); 107 | try { 108 | $form.data("validator").resetForm(); 109 | } finally { 110 | $form.removeData(key); 111 | } 112 | 113 | $form.find(".validation-summary-errors") 114 | .addClass("validation-summary-valid") 115 | .removeClass("validation-summary-errors"); 116 | $form.find(".field-validation-error") 117 | .addClass("field-validation-valid") 118 | .removeClass("field-validation-error") 119 | .removeData("unobtrusiveContainer") 120 | .find(">*") // If we were using valmsg-replace, get the underlying error 121 | .removeData("unobtrusiveContainer"); 122 | } 123 | 124 | function validationInfo(form) { 125 | var $form = $(form), 126 | result = $form.data(data_validation), 127 | onResetProxy = $.proxy(onReset, form), 128 | defaultOptions = $jQval.unobtrusive.options || {}, 129 | execInContext = function (name, args) { 130 | var func = defaultOptions[name]; 131 | func && $.isFunction(func) && func.apply(form, args); 132 | }; 133 | 134 | if (!result) { 135 | result = { 136 | options: { // options structure passed to jQuery Validate's validate() method 137 | errorClass: defaultOptions.errorClass || "input-validation-error", 138 | errorElement: defaultOptions.errorElement || "span", 139 | errorPlacement: function () { 140 | onError.apply(form, arguments); 141 | execInContext("errorPlacement", arguments); 142 | }, 143 | invalidHandler: function () { 144 | onErrors.apply(form, arguments); 145 | execInContext("invalidHandler", arguments); 146 | }, 147 | messages: {}, 148 | rules: {}, 149 | success: function () { 150 | onSuccess.apply(form, arguments); 151 | execInContext("success", arguments); 152 | } 153 | }, 154 | attachValidation: function () { 155 | $form 156 | .off("reset." + data_validation, onResetProxy) 157 | .on("reset." + data_validation, onResetProxy) 158 | .validate(this.options); 159 | }, 160 | validate: function () { // a validation function that is called by unobtrusive Ajax 161 | $form.validate(); 162 | return $form.valid(); 163 | } 164 | }; 165 | $form.data(data_validation, result); 166 | } 167 | 168 | return result; 169 | } 170 | 171 | $jQval.unobtrusive = { 172 | adapters: [], 173 | 174 | parseElement: function (element, skipAttach) { 175 | /// 176 | /// Parses a single HTML element for unobtrusive validation attributes. 177 | /// 178 | /// The HTML element to be parsed. 179 | /// [Optional] true to skip attaching the 180 | /// validation to the form. If parsing just this single element, you should specify true. 181 | /// If parsing several elements, you should specify false, and manually attach the validation 182 | /// to the form when you are finished. The default is false. 183 | var $element = $(element), 184 | form = $element.parents("form")[0], 185 | valInfo, rules, messages; 186 | 187 | if (!form) { // Cannot do client-side validation without a form 188 | return; 189 | } 190 | 191 | valInfo = validationInfo(form); 192 | valInfo.options.rules[element.name] = rules = {}; 193 | valInfo.options.messages[element.name] = messages = {}; 194 | 195 | $.each(this.adapters, function () { 196 | var prefix = "data-val-" + this.name, 197 | message = $element.attr(prefix), 198 | paramValues = {}; 199 | 200 | if (message !== undefined) { // Compare against undefined, because an empty message is legal (and falsy) 201 | prefix += "-"; 202 | 203 | $.each(this.params, function () { 204 | paramValues[this] = $element.attr(prefix + this); 205 | }); 206 | 207 | this.adapt({ 208 | element: element, 209 | form: form, 210 | message: message, 211 | params: paramValues, 212 | rules: rules, 213 | messages: messages 214 | }); 215 | } 216 | }); 217 | 218 | $.extend(rules, { "__dummy__": true }); 219 | 220 | if (!skipAttach) { 221 | valInfo.attachValidation(); 222 | } 223 | }, 224 | 225 | parse: function (selector) { 226 | /// 227 | /// Parses all the HTML elements in the specified selector. It looks for input elements decorated 228 | /// with the [data-val=true] attribute value and enables validation according to the data-val-* 229 | /// attribute values. 230 | /// 231 | /// Any valid jQuery selector. 232 | 233 | // $forms includes all forms in selector's DOM hierarchy (parent, children and self) that have at least one 234 | // element with data-val=true 235 | var $selector = $(selector), 236 | $forms = $selector.parents() 237 | .addBack() 238 | .filter("form") 239 | .add($selector.find("form")) 240 | .has("[data-val=true]"); 241 | 242 | $selector.find("[data-val=true]").each(function () { 243 | $jQval.unobtrusive.parseElement(this, true); 244 | }); 245 | 246 | $forms.each(function () { 247 | var info = validationInfo(this); 248 | if (info) { 249 | info.attachValidation(); 250 | } 251 | }); 252 | } 253 | }; 254 | 255 | adapters = $jQval.unobtrusive.adapters; 256 | 257 | adapters.add = function (adapterName, params, fn) { 258 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation. 259 | /// The name of the adapter to be added. This matches the name used 260 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 261 | /// [Optional] An array of parameter names (strings) that will 262 | /// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and 263 | /// mmmm is the parameter name). 264 | /// The function to call, which adapts the values from the HTML 265 | /// attributes into jQuery Validate rules and/or messages. 266 | /// 267 | if (!fn) { // Called with no params, just a function 268 | fn = params; 269 | params = []; 270 | } 271 | this.push({ name: adapterName, params: params, adapt: fn }); 272 | return this; 273 | }; 274 | 275 | adapters.addBool = function (adapterName, ruleName) { 276 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 277 | /// the jQuery Validate validation rule has no parameter values. 278 | /// The name of the adapter to be added. This matches the name used 279 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 280 | /// [Optional] The name of the jQuery Validate rule. If not provided, the value 281 | /// of adapterName will be used instead. 282 | /// 283 | return this.add(adapterName, function (options) { 284 | setValidationValues(options, ruleName || adapterName, true); 285 | }); 286 | }; 287 | 288 | adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) { 289 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 290 | /// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and 291 | /// one for min-and-max). The HTML parameters are expected to be named -min and -max. 292 | /// The name of the adapter to be added. This matches the name used 293 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 294 | /// The name of the jQuery Validate rule to be used when you only 295 | /// have a minimum value. 296 | /// The name of the jQuery Validate rule to be used when you only 297 | /// have a maximum value. 298 | /// The name of the jQuery Validate rule to be used when you 299 | /// have both a minimum and maximum value. 300 | /// [Optional] The name of the HTML attribute that 301 | /// contains the minimum value. The default is "min". 302 | /// [Optional] The name of the HTML attribute that 303 | /// contains the maximum value. The default is "max". 304 | /// 305 | return this.add(adapterName, [minAttribute || "min", maxAttribute || "max"], function (options) { 306 | var min = options.params.min, 307 | max = options.params.max; 308 | 309 | if (min && max) { 310 | setValidationValues(options, minMaxRuleName, [min, max]); 311 | } 312 | else if (min) { 313 | setValidationValues(options, minRuleName, min); 314 | } 315 | else if (max) { 316 | setValidationValues(options, maxRuleName, max); 317 | } 318 | }); 319 | }; 320 | 321 | adapters.addSingleVal = function (adapterName, attribute, ruleName) { 322 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 323 | /// the jQuery Validate validation rule has a single value. 324 | /// The name of the adapter to be added. This matches the name used 325 | /// in the data-val-nnnn HTML attribute(where nnnn is the adapter name). 326 | /// [Optional] The name of the HTML attribute that contains the value. 327 | /// The default is "val". 328 | /// [Optional] The name of the jQuery Validate rule. If not provided, the value 329 | /// of adapterName will be used instead. 330 | /// 331 | return this.add(adapterName, [attribute || "val"], function (options) { 332 | setValidationValues(options, ruleName || adapterName, options.params[attribute]); 333 | }); 334 | }; 335 | 336 | $jQval.addMethod("__dummy__", function (value, element, params) { 337 | return true; 338 | }); 339 | 340 | $jQval.addMethod("regex", function (value, element, params) { 341 | var match; 342 | if (this.optional(element)) { 343 | return true; 344 | } 345 | 346 | match = new RegExp(params).exec(value); 347 | return (match && (match.index === 0) && (match[0].length === value.length)); 348 | }); 349 | 350 | $jQval.addMethod("nonalphamin", function (value, element, nonalphamin) { 351 | var match; 352 | if (nonalphamin) { 353 | match = value.match(/\W/g); 354 | match = match && match.length >= nonalphamin; 355 | } 356 | return match; 357 | }); 358 | 359 | if ($jQval.methods.extension) { 360 | adapters.addSingleVal("accept", "mimtype"); 361 | adapters.addSingleVal("extension", "extension"); 362 | } else { 363 | // for backward compatibility, when the 'extension' validation method does not exist, such as with versions 364 | // of JQuery Validation plugin prior to 1.10, we should use the 'accept' method for 365 | // validating the extension, and ignore mime-type validations as they are not supported. 366 | adapters.addSingleVal("extension", "extension", "accept"); 367 | } 368 | 369 | adapters.addSingleVal("regex", "pattern"); 370 | adapters.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"); 371 | adapters.addMinMax("length", "minlength", "maxlength", "rangelength").addMinMax("range", "min", "max", "range"); 372 | adapters.addMinMax("minlength", "minlength").addMinMax("maxlength", "minlength", "maxlength"); 373 | adapters.add("equalto", ["other"], function (options) { 374 | var prefix = getModelPrefix(options.element.name), 375 | other = options.params.other, 376 | fullOtherName = appendModelPrefix(other, prefix), 377 | element = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(fullOtherName) + "']")[0]; 378 | 379 | setValidationValues(options, "equalTo", element); 380 | }); 381 | adapters.add("required", function (options) { 382 | // jQuery Validate equates "required" with "mandatory" for checkbox elements 383 | if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") { 384 | setValidationValues(options, "required", true); 385 | } 386 | }); 387 | adapters.add("remote", ["url", "type", "additionalfields"], function (options) { 388 | var value = { 389 | url: options.params.url, 390 | type: options.params.type || "GET", 391 | data: {} 392 | }, 393 | prefix = getModelPrefix(options.element.name); 394 | 395 | $.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) { 396 | var paramName = appendModelPrefix(fieldName, prefix); 397 | value.data[paramName] = function () { 398 | var field = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(paramName) + "']"); 399 | // For checkboxes and radio buttons, only pick up values from checked fields. 400 | if (field.is(":checkbox")) { 401 | return field.filter(":checked").val() || field.filter(":hidden").val() || ''; 402 | } 403 | else if (field.is(":radio")) { 404 | return field.filter(":checked").val() || ''; 405 | } 406 | return field.val(); 407 | }; 408 | }); 409 | 410 | setValidationValues(options, "remote", value); 411 | }); 412 | adapters.add("password", ["min", "nonalphamin", "regex"], function (options) { 413 | if (options.params.min) { 414 | setValidationValues(options, "minlength", options.params.min); 415 | } 416 | if (options.params.nonalphamin) { 417 | setValidationValues(options, "nonalphamin", options.params.nonalphamin); 418 | } 419 | if (options.params.regex) { 420 | setValidationValues(options, "regex", options.params.regex); 421 | } 422 | }); 423 | adapters.add("fileextensions", ["extensions"], function (options) { 424 | setValidationValues(options, "extension", options.params.extensions); 425 | }); 426 | 427 | $(function () { 428 | $jQval.unobtrusive.parse(document); 429 | }); 430 | 431 | return $jQval.unobtrusive; 432 | })); 433 | -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | // Unobtrusive validation support library for jQuery and jQuery Validate 2 | // Copyright (c) .NET Foundation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 4 | // @version v3.2.11 5 | !function(a){"function"==typeof define&&define.amd?define("jquery.validate.unobtrusive",["jquery-validation"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery-validation")):jQuery.validator.unobtrusive=a(jQuery)}(function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a("
  • ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function u(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=f.unobtrusive.options||{},u=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),u("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),u("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),u("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var m,f=a.validator,v="unobtrusiveValidation";return f.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=u(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){f.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=u(this);a&&a.attachValidation()})}},m=f.unobtrusive.adapters,m.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},m.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},m.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},m.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},f.addMethod("__dummy__",function(a,e,n){return!0}),f.addMethod("regex",function(a,e,n){var t;return!!this.optional(e)||(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),f.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),f.methods.extension?(m.addSingleVal("accept","mimtype"),m.addSingleVal("extension","extension")):m.addSingleVal("extension","extension","accept"),m.addSingleVal("regex","pattern"),m.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),m.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),m.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),m.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),m.add("required",function(a){"INPUT"===a.element.tagName.toUpperCase()&&"CHECKBOX"===a.element.type.toUpperCase()||e(a,"required",!0)}),m.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),m.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),m.add("fileextensions",["extensions"],function(a){e(a,"extension",a.params.extensions)}),a(function(){f.unobtrusive.parse(document)}),f.unobtrusive}); -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/lib/jquery-validation/dist/additional-methods.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Validation Plugin v1.17.0 3 | * 4 | * https://jqueryvalidation.org/ 5 | * 6 | * Copyright (c) 2017 Jörn Zaefferer 7 | * Released under the MIT license 8 | */ 9 | (function( factory ) { 10 | if ( typeof define === "function" && define.amd ) { 11 | define( ["jquery", "./jquery.validate"], factory ); 12 | } else if (typeof module === "object" && module.exports) { 13 | module.exports = factory( require( "jquery" ) ); 14 | } else { 15 | factory( jQuery ); 16 | } 17 | }(function( $ ) { 18 | 19 | ( function() { 20 | 21 | function stripHtml( value ) { 22 | 23 | // Remove html tags and space chars 24 | return value.replace( /<.[^<>]*?>/g, " " ).replace( / | /gi, " " ) 25 | 26 | // Remove punctuation 27 | .replace( /[.(),;:!?%#$'\"_+=\/\-“”’]*/g, "" ); 28 | } 29 | 30 | $.validator.addMethod( "maxWords", function( value, element, params ) { 31 | return this.optional( element ) || stripHtml( value ).match( /\b\w+\b/g ).length <= params; 32 | }, $.validator.format( "Please enter {0} words or less." ) ); 33 | 34 | $.validator.addMethod( "minWords", function( value, element, params ) { 35 | return this.optional( element ) || stripHtml( value ).match( /\b\w+\b/g ).length >= params; 36 | }, $.validator.format( "Please enter at least {0} words." ) ); 37 | 38 | $.validator.addMethod( "rangeWords", function( value, element, params ) { 39 | var valueStripped = stripHtml( value ), 40 | regex = /\b\w+\b/g; 41 | return this.optional( element ) || valueStripped.match( regex ).length >= params[ 0 ] && valueStripped.match( regex ).length <= params[ 1 ]; 42 | }, $.validator.format( "Please enter between {0} and {1} words." ) ); 43 | 44 | }() ); 45 | 46 | // Accept a value from a file input based on a required mimetype 47 | $.validator.addMethod( "accept", function( value, element, param ) { 48 | 49 | // Split mime on commas in case we have multiple types we can accept 50 | var typeParam = typeof param === "string" ? param.replace( /\s/g, "" ) : "image/*", 51 | optionalValue = this.optional( element ), 52 | i, file, regex; 53 | 54 | // Element is optional 55 | if ( optionalValue ) { 56 | return optionalValue; 57 | } 58 | 59 | if ( $( element ).attr( "type" ) === "file" ) { 60 | 61 | // Escape string to be used in the regex 62 | // see: https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex 63 | // Escape also "/*" as "/.*" as a wildcard 64 | typeParam = typeParam 65 | .replace( /[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g, "\\$&" ) 66 | .replace( /,/g, "|" ) 67 | .replace( /\/\*/g, "/.*" ); 68 | 69 | // Check if the element has a FileList before checking each file 70 | if ( element.files && element.files.length ) { 71 | regex = new RegExp( ".?(" + typeParam + ")$", "i" ); 72 | for ( i = 0; i < element.files.length; i++ ) { 73 | file = element.files[ i ]; 74 | 75 | // Grab the mimetype from the loaded file, verify it matches 76 | if ( !file.type.match( regex ) ) { 77 | return false; 78 | } 79 | } 80 | } 81 | } 82 | 83 | // Either return true because we've validated each file, or because the 84 | // browser does not support element.files and the FileList feature 85 | return true; 86 | }, $.validator.format( "Please enter a value with a valid mimetype." ) ); 87 | 88 | $.validator.addMethod( "alphanumeric", function( value, element ) { 89 | return this.optional( element ) || /^\w+$/i.test( value ); 90 | }, "Letters, numbers, and underscores only please" ); 91 | 92 | /* 93 | * Dutch bank account numbers (not 'giro' numbers) have 9 digits 94 | * and pass the '11 check'. 95 | * We accept the notation with spaces, as that is common. 96 | * acceptable: 123456789 or 12 34 56 789 97 | */ 98 | $.validator.addMethod( "bankaccountNL", function( value, element ) { 99 | if ( this.optional( element ) ) { 100 | return true; 101 | } 102 | if ( !( /^[0-9]{9}|([0-9]{2} ){3}[0-9]{3}$/.test( value ) ) ) { 103 | return false; 104 | } 105 | 106 | // Now '11 check' 107 | var account = value.replace( / /g, "" ), // Remove spaces 108 | sum = 0, 109 | len = account.length, 110 | pos, factor, digit; 111 | for ( pos = 0; pos < len; pos++ ) { 112 | factor = len - pos; 113 | digit = account.substring( pos, pos + 1 ); 114 | sum = sum + factor * digit; 115 | } 116 | return sum % 11 === 0; 117 | }, "Please specify a valid bank account number" ); 118 | 119 | $.validator.addMethod( "bankorgiroaccountNL", function( value, element ) { 120 | return this.optional( element ) || 121 | ( $.validator.methods.bankaccountNL.call( this, value, element ) ) || 122 | ( $.validator.methods.giroaccountNL.call( this, value, element ) ); 123 | }, "Please specify a valid bank or giro account number" ); 124 | 125 | /** 126 | * BIC is the business identifier code (ISO 9362). This BIC check is not a guarantee for authenticity. 127 | * 128 | * BIC pattern: BBBBCCLLbbb (8 or 11 characters long; bbb is optional) 129 | * 130 | * Validation is case-insensitive. Please make sure to normalize input yourself. 131 | * 132 | * BIC definition in detail: 133 | * - First 4 characters - bank code (only letters) 134 | * - Next 2 characters - ISO 3166-1 alpha-2 country code (only letters) 135 | * - Next 2 characters - location code (letters and digits) 136 | * a. shall not start with '0' or '1' 137 | * b. second character must be a letter ('O' is not allowed) or digit ('0' for test (therefore not allowed), '1' denoting passive participant, '2' typically reverse-billing) 138 | * - Last 3 characters - branch code, optional (shall not start with 'X' except in case of 'XXX' for primary office) (letters and digits) 139 | */ 140 | $.validator.addMethod( "bic", function( value, element ) { 141 | return this.optional( element ) || /^([A-Z]{6}[A-Z2-9][A-NP-Z1-9])(X{3}|[A-WY-Z0-9][A-Z0-9]{2})?$/.test( value.toUpperCase() ); 142 | }, "Please specify a valid BIC code" ); 143 | 144 | /* 145 | * Código de identificación fiscal ( CIF ) is the tax identification code for Spanish legal entities 146 | * Further rules can be found in Spanish on http://es.wikipedia.org/wiki/C%C3%B3digo_de_identificaci%C3%B3n_fiscal 147 | * 148 | * Spanish CIF structure: 149 | * 150 | * [ T ][ P ][ P ][ N ][ N ][ N ][ N ][ N ][ C ] 151 | * 152 | * Where: 153 | * 154 | * T: 1 character. Kind of Organization Letter: [ABCDEFGHJKLMNPQRSUVW] 155 | * P: 2 characters. Province. 156 | * N: 5 characters. Secuencial Number within the province. 157 | * C: 1 character. Control Digit: [0-9A-J]. 158 | * 159 | * [ T ]: Kind of Organizations. Possible values: 160 | * 161 | * A. Corporations 162 | * B. LLCs 163 | * C. General partnerships 164 | * D. Companies limited partnerships 165 | * E. Communities of goods 166 | * F. Cooperative Societies 167 | * G. Associations 168 | * H. Communities of homeowners in horizontal property regime 169 | * J. Civil Societies 170 | * K. Old format 171 | * L. Old format 172 | * M. Old format 173 | * N. Nonresident entities 174 | * P. Local authorities 175 | * Q. Autonomous bodies, state or not, and the like, and congregations and religious institutions 176 | * R. Congregations and religious institutions (since 2008 ORDER EHA/451/2008) 177 | * S. Organs of State Administration and regions 178 | * V. Agrarian Transformation 179 | * W. Permanent establishments of non-resident in Spain 180 | * 181 | * [ C ]: Control Digit. It can be a number or a letter depending on T value: 182 | * [ T ] --> [ C ] 183 | * ------ ---------- 184 | * A Number 185 | * B Number 186 | * E Number 187 | * H Number 188 | * K Letter 189 | * P Letter 190 | * Q Letter 191 | * S Letter 192 | * 193 | */ 194 | $.validator.addMethod( "cifES", function( value, element ) { 195 | "use strict"; 196 | 197 | if ( this.optional( element ) ) { 198 | return true; 199 | } 200 | 201 | var cifRegEx = new RegExp( /^([ABCDEFGHJKLMNPQRSUVW])(\d{7})([0-9A-J])$/gi ); 202 | var letter = value.substring( 0, 1 ), // [ T ] 203 | number = value.substring( 1, 8 ), // [ P ][ P ][ N ][ N ][ N ][ N ][ N ] 204 | control = value.substring( 8, 9 ), // [ C ] 205 | all_sum = 0, 206 | even_sum = 0, 207 | odd_sum = 0, 208 | i, n, 209 | control_digit, 210 | control_letter; 211 | 212 | function isOdd( n ) { 213 | return n % 2 === 0; 214 | } 215 | 216 | // Quick format test 217 | if ( value.length !== 9 || !cifRegEx.test( value ) ) { 218 | return false; 219 | } 220 | 221 | for ( i = 0; i < number.length; i++ ) { 222 | n = parseInt( number[ i ], 10 ); 223 | 224 | // Odd positions 225 | if ( isOdd( i ) ) { 226 | 227 | // Odd positions are multiplied first. 228 | n *= 2; 229 | 230 | // If the multiplication is bigger than 10 we need to adjust 231 | odd_sum += n < 10 ? n : n - 9; 232 | 233 | // Even positions 234 | // Just sum them 235 | } else { 236 | even_sum += n; 237 | } 238 | } 239 | 240 | all_sum = even_sum + odd_sum; 241 | control_digit = ( 10 - ( all_sum ).toString().substr( -1 ) ).toString(); 242 | control_digit = parseInt( control_digit, 10 ) > 9 ? "0" : control_digit; 243 | control_letter = "JABCDEFGHI".substr( control_digit, 1 ).toString(); 244 | 245 | // Control must be a digit 246 | if ( letter.match( /[ABEH]/ ) ) { 247 | return control === control_digit; 248 | 249 | // Control must be a letter 250 | } else if ( letter.match( /[KPQS]/ ) ) { 251 | return control === control_letter; 252 | } 253 | 254 | // Can be either 255 | return control === control_digit || control === control_letter; 256 | 257 | }, "Please specify a valid CIF number." ); 258 | 259 | /* 260 | * Brazillian CPF number (Cadastrado de Pessoas Físicas) is the equivalent of a Brazilian tax registration number. 261 | * CPF numbers have 11 digits in total: 9 numbers followed by 2 check numbers that are being used for validation. 262 | */ 263 | $.validator.addMethod( "cpfBR", function( value ) { 264 | 265 | // Removing special characters from value 266 | value = value.replace( /([~!@#$%^&*()_+=`{}\[\]\-|\\:;'<>,.\/? ])+/g, "" ); 267 | 268 | // Checking value to have 11 digits only 269 | if ( value.length !== 11 ) { 270 | return false; 271 | } 272 | 273 | var sum = 0, 274 | firstCN, secondCN, checkResult, i; 275 | 276 | firstCN = parseInt( value.substring( 9, 10 ), 10 ); 277 | secondCN = parseInt( value.substring( 10, 11 ), 10 ); 278 | 279 | checkResult = function( sum, cn ) { 280 | var result = ( sum * 10 ) % 11; 281 | if ( ( result === 10 ) || ( result === 11 ) ) { 282 | result = 0; 283 | } 284 | return ( result === cn ); 285 | }; 286 | 287 | // Checking for dump data 288 | if ( value === "" || 289 | value === "00000000000" || 290 | value === "11111111111" || 291 | value === "22222222222" || 292 | value === "33333333333" || 293 | value === "44444444444" || 294 | value === "55555555555" || 295 | value === "66666666666" || 296 | value === "77777777777" || 297 | value === "88888888888" || 298 | value === "99999999999" 299 | ) { 300 | return false; 301 | } 302 | 303 | // Step 1 - using first Check Number: 304 | for ( i = 1; i <= 9; i++ ) { 305 | sum = sum + parseInt( value.substring( i - 1, i ), 10 ) * ( 11 - i ); 306 | } 307 | 308 | // If first Check Number (CN) is valid, move to Step 2 - using second Check Number: 309 | if ( checkResult( sum, firstCN ) ) { 310 | sum = 0; 311 | for ( i = 1; i <= 10; i++ ) { 312 | sum = sum + parseInt( value.substring( i - 1, i ), 10 ) * ( 12 - i ); 313 | } 314 | return checkResult( sum, secondCN ); 315 | } 316 | return false; 317 | 318 | }, "Please specify a valid CPF number" ); 319 | 320 | // https://jqueryvalidation.org/creditcard-method/ 321 | // based on https://en.wikipedia.org/wiki/Luhn_algorithm 322 | $.validator.addMethod( "creditcard", function( value, element ) { 323 | if ( this.optional( element ) ) { 324 | return "dependency-mismatch"; 325 | } 326 | 327 | // Accept only spaces, digits and dashes 328 | if ( /[^0-9 \-]+/.test( value ) ) { 329 | return false; 330 | } 331 | 332 | var nCheck = 0, 333 | nDigit = 0, 334 | bEven = false, 335 | n, cDigit; 336 | 337 | value = value.replace( /\D/g, "" ); 338 | 339 | // Basing min and max length on 340 | // https://developer.ean.com/general_info/Valid_Credit_Card_Types 341 | if ( value.length < 13 || value.length > 19 ) { 342 | return false; 343 | } 344 | 345 | for ( n = value.length - 1; n >= 0; n-- ) { 346 | cDigit = value.charAt( n ); 347 | nDigit = parseInt( cDigit, 10 ); 348 | if ( bEven ) { 349 | if ( ( nDigit *= 2 ) > 9 ) { 350 | nDigit -= 9; 351 | } 352 | } 353 | 354 | nCheck += nDigit; 355 | bEven = !bEven; 356 | } 357 | 358 | return ( nCheck % 10 ) === 0; 359 | }, "Please enter a valid credit card number." ); 360 | 361 | /* NOTICE: Modified version of Castle.Components.Validator.CreditCardValidator 362 | * Redistributed under the the Apache License 2.0 at http://www.apache.org/licenses/LICENSE-2.0 363 | * Valid Types: mastercard, visa, amex, dinersclub, enroute, discover, jcb, unknown, all (overrides all other settings) 364 | */ 365 | $.validator.addMethod( "creditcardtypes", function( value, element, param ) { 366 | if ( /[^0-9\-]+/.test( value ) ) { 367 | return false; 368 | } 369 | 370 | value = value.replace( /\D/g, "" ); 371 | 372 | var validTypes = 0x0000; 373 | 374 | if ( param.mastercard ) { 375 | validTypes |= 0x0001; 376 | } 377 | if ( param.visa ) { 378 | validTypes |= 0x0002; 379 | } 380 | if ( param.amex ) { 381 | validTypes |= 0x0004; 382 | } 383 | if ( param.dinersclub ) { 384 | validTypes |= 0x0008; 385 | } 386 | if ( param.enroute ) { 387 | validTypes |= 0x0010; 388 | } 389 | if ( param.discover ) { 390 | validTypes |= 0x0020; 391 | } 392 | if ( param.jcb ) { 393 | validTypes |= 0x0040; 394 | } 395 | if ( param.unknown ) { 396 | validTypes |= 0x0080; 397 | } 398 | if ( param.all ) { 399 | validTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080; 400 | } 401 | if ( validTypes & 0x0001 && /^(5[12345])/.test( value ) ) { // Mastercard 402 | return value.length === 16; 403 | } 404 | if ( validTypes & 0x0002 && /^(4)/.test( value ) ) { // Visa 405 | return value.length === 16; 406 | } 407 | if ( validTypes & 0x0004 && /^(3[47])/.test( value ) ) { // Amex 408 | return value.length === 15; 409 | } 410 | if ( validTypes & 0x0008 && /^(3(0[012345]|[68]))/.test( value ) ) { // Dinersclub 411 | return value.length === 14; 412 | } 413 | if ( validTypes & 0x0010 && /^(2(014|149))/.test( value ) ) { // Enroute 414 | return value.length === 15; 415 | } 416 | if ( validTypes & 0x0020 && /^(6011)/.test( value ) ) { // Discover 417 | return value.length === 16; 418 | } 419 | if ( validTypes & 0x0040 && /^(3)/.test( value ) ) { // Jcb 420 | return value.length === 16; 421 | } 422 | if ( validTypes & 0x0040 && /^(2131|1800)/.test( value ) ) { // Jcb 423 | return value.length === 15; 424 | } 425 | if ( validTypes & 0x0080 ) { // Unknown 426 | return true; 427 | } 428 | return false; 429 | }, "Please enter a valid credit card number." ); 430 | 431 | /** 432 | * Validates currencies with any given symbols by @jameslouiz 433 | * Symbols can be optional or required. Symbols required by default 434 | * 435 | * Usage examples: 436 | * currency: ["£", false] - Use false for soft currency validation 437 | * currency: ["$", false] 438 | * currency: ["RM", false] - also works with text based symbols such as "RM" - Malaysia Ringgit etc 439 | * 440 | * 441 | * 442 | * Soft symbol checking 443 | * currencyInput: { 444 | * currency: ["$", false] 445 | * } 446 | * 447 | * Strict symbol checking (default) 448 | * currencyInput: { 449 | * currency: "$" 450 | * //OR 451 | * currency: ["$", true] 452 | * } 453 | * 454 | * Multiple Symbols 455 | * currencyInput: { 456 | * currency: "$,£,¢" 457 | * } 458 | */ 459 | $.validator.addMethod( "currency", function( value, element, param ) { 460 | var isParamString = typeof param === "string", 461 | symbol = isParamString ? param : param[ 0 ], 462 | soft = isParamString ? true : param[ 1 ], 463 | regex; 464 | 465 | symbol = symbol.replace( /,/g, "" ); 466 | symbol = soft ? symbol + "]" : symbol + "]?"; 467 | regex = "^[" + symbol + "([1-9]{1}[0-9]{0,2}(\\,[0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)$"; 468 | regex = new RegExp( regex ); 469 | return this.optional( element ) || regex.test( value ); 470 | 471 | }, "Please specify a valid currency" ); 472 | 473 | $.validator.addMethod( "dateFA", function( value, element ) { 474 | return this.optional( element ) || /^[1-4]\d{3}\/((0?[1-6]\/((3[0-1])|([1-2][0-9])|(0?[1-9])))|((1[0-2]|(0?[7-9]))\/(30|([1-2][0-9])|(0?[1-9]))))$/.test( value ); 475 | }, $.validator.messages.date ); 476 | 477 | /** 478 | * Return true, if the value is a valid date, also making this formal check dd/mm/yyyy. 479 | * 480 | * @example $.validator.methods.date("01/01/1900") 481 | * @result true 482 | * 483 | * @example $.validator.methods.date("01/13/1990") 484 | * @result false 485 | * 486 | * @example $.validator.methods.date("01.01.1900") 487 | * @result false 488 | * 489 | * @example 490 | * @desc Declares an optional input element whose value must be a valid date. 491 | * 492 | * @name $.validator.methods.dateITA 493 | * @type Boolean 494 | * @cat Plugins/Validate/Methods 495 | */ 496 | $.validator.addMethod( "dateITA", function( value, element ) { 497 | var check = false, 498 | re = /^\d{1,2}\/\d{1,2}\/\d{4}$/, 499 | adata, gg, mm, aaaa, xdata; 500 | if ( re.test( value ) ) { 501 | adata = value.split( "/" ); 502 | gg = parseInt( adata[ 0 ], 10 ); 503 | mm = parseInt( adata[ 1 ], 10 ); 504 | aaaa = parseInt( adata[ 2 ], 10 ); 505 | xdata = new Date( Date.UTC( aaaa, mm - 1, gg, 12, 0, 0, 0 ) ); 506 | if ( ( xdata.getUTCFullYear() === aaaa ) && ( xdata.getUTCMonth() === mm - 1 ) && ( xdata.getUTCDate() === gg ) ) { 507 | check = true; 508 | } else { 509 | check = false; 510 | } 511 | } else { 512 | check = false; 513 | } 514 | return this.optional( element ) || check; 515 | }, $.validator.messages.date ); 516 | 517 | $.validator.addMethod( "dateNL", function( value, element ) { 518 | return this.optional( element ) || /^(0?[1-9]|[12]\d|3[01])[\.\/\-](0?[1-9]|1[012])[\.\/\-]([12]\d)?(\d\d)$/.test( value ); 519 | }, $.validator.messages.date ); 520 | 521 | // Older "accept" file extension method. Old docs: http://docs.jquery.com/Plugins/Validation/Methods/accept 522 | $.validator.addMethod( "extension", function( value, element, param ) { 523 | param = typeof param === "string" ? param.replace( /,/g, "|" ) : "png|jpe?g|gif"; 524 | return this.optional( element ) || value.match( new RegExp( "\\.(" + param + ")$", "i" ) ); 525 | }, $.validator.format( "Please enter a value with a valid extension." ) ); 526 | 527 | /** 528 | * Dutch giro account numbers (not bank numbers) have max 7 digits 529 | */ 530 | $.validator.addMethod( "giroaccountNL", function( value, element ) { 531 | return this.optional( element ) || /^[0-9]{1,7}$/.test( value ); 532 | }, "Please specify a valid giro account number" ); 533 | 534 | /** 535 | * IBAN is the international bank account number. 536 | * It has a country - specific format, that is checked here too 537 | * 538 | * Validation is case-insensitive. Please make sure to normalize input yourself. 539 | */ 540 | $.validator.addMethod( "iban", function( value, element ) { 541 | 542 | // Some quick simple tests to prevent needless work 543 | if ( this.optional( element ) ) { 544 | return true; 545 | } 546 | 547 | // Remove spaces and to upper case 548 | var iban = value.replace( / /g, "" ).toUpperCase(), 549 | ibancheckdigits = "", 550 | leadingZeroes = true, 551 | cRest = "", 552 | cOperator = "", 553 | countrycode, ibancheck, charAt, cChar, bbanpattern, bbancountrypatterns, ibanregexp, i, p; 554 | 555 | // Check for IBAN code length. 556 | // It contains: 557 | // country code ISO 3166-1 - two letters, 558 | // two check digits, 559 | // Basic Bank Account Number (BBAN) - up to 30 chars 560 | var minimalIBANlength = 5; 561 | if ( iban.length < minimalIBANlength ) { 562 | return false; 563 | } 564 | 565 | // Check the country code and find the country specific format 566 | countrycode = iban.substring( 0, 2 ); 567 | bbancountrypatterns = { 568 | "AL": "\\d{8}[\\dA-Z]{16}", 569 | "AD": "\\d{8}[\\dA-Z]{12}", 570 | "AT": "\\d{16}", 571 | "AZ": "[\\dA-Z]{4}\\d{20}", 572 | "BE": "\\d{12}", 573 | "BH": "[A-Z]{4}[\\dA-Z]{14}", 574 | "BA": "\\d{16}", 575 | "BR": "\\d{23}[A-Z][\\dA-Z]", 576 | "BG": "[A-Z]{4}\\d{6}[\\dA-Z]{8}", 577 | "CR": "\\d{17}", 578 | "HR": "\\d{17}", 579 | "CY": "\\d{8}[\\dA-Z]{16}", 580 | "CZ": "\\d{20}", 581 | "DK": "\\d{14}", 582 | "DO": "[A-Z]{4}\\d{20}", 583 | "EE": "\\d{16}", 584 | "FO": "\\d{14}", 585 | "FI": "\\d{14}", 586 | "FR": "\\d{10}[\\dA-Z]{11}\\d{2}", 587 | "GE": "[\\dA-Z]{2}\\d{16}", 588 | "DE": "\\d{18}", 589 | "GI": "[A-Z]{4}[\\dA-Z]{15}", 590 | "GR": "\\d{7}[\\dA-Z]{16}", 591 | "GL": "\\d{14}", 592 | "GT": "[\\dA-Z]{4}[\\dA-Z]{20}", 593 | "HU": "\\d{24}", 594 | "IS": "\\d{22}", 595 | "IE": "[\\dA-Z]{4}\\d{14}", 596 | "IL": "\\d{19}", 597 | "IT": "[A-Z]\\d{10}[\\dA-Z]{12}", 598 | "KZ": "\\d{3}[\\dA-Z]{13}", 599 | "KW": "[A-Z]{4}[\\dA-Z]{22}", 600 | "LV": "[A-Z]{4}[\\dA-Z]{13}", 601 | "LB": "\\d{4}[\\dA-Z]{20}", 602 | "LI": "\\d{5}[\\dA-Z]{12}", 603 | "LT": "\\d{16}", 604 | "LU": "\\d{3}[\\dA-Z]{13}", 605 | "MK": "\\d{3}[\\dA-Z]{10}\\d{2}", 606 | "MT": "[A-Z]{4}\\d{5}[\\dA-Z]{18}", 607 | "MR": "\\d{23}", 608 | "MU": "[A-Z]{4}\\d{19}[A-Z]{3}", 609 | "MC": "\\d{10}[\\dA-Z]{11}\\d{2}", 610 | "MD": "[\\dA-Z]{2}\\d{18}", 611 | "ME": "\\d{18}", 612 | "NL": "[A-Z]{4}\\d{10}", 613 | "NO": "\\d{11}", 614 | "PK": "[\\dA-Z]{4}\\d{16}", 615 | "PS": "[\\dA-Z]{4}\\d{21}", 616 | "PL": "\\d{24}", 617 | "PT": "\\d{21}", 618 | "RO": "[A-Z]{4}[\\dA-Z]{16}", 619 | "SM": "[A-Z]\\d{10}[\\dA-Z]{12}", 620 | "SA": "\\d{2}[\\dA-Z]{18}", 621 | "RS": "\\d{18}", 622 | "SK": "\\d{20}", 623 | "SI": "\\d{15}", 624 | "ES": "\\d{20}", 625 | "SE": "\\d{20}", 626 | "CH": "\\d{5}[\\dA-Z]{12}", 627 | "TN": "\\d{20}", 628 | "TR": "\\d{5}[\\dA-Z]{17}", 629 | "AE": "\\d{3}\\d{16}", 630 | "GB": "[A-Z]{4}\\d{14}", 631 | "VG": "[\\dA-Z]{4}\\d{16}" 632 | }; 633 | 634 | bbanpattern = bbancountrypatterns[ countrycode ]; 635 | 636 | // As new countries will start using IBAN in the 637 | // future, we only check if the countrycode is known. 638 | // This prevents false negatives, while almost all 639 | // false positives introduced by this, will be caught 640 | // by the checksum validation below anyway. 641 | // Strict checking should return FALSE for unknown 642 | // countries. 643 | if ( typeof bbanpattern !== "undefined" ) { 644 | ibanregexp = new RegExp( "^[A-Z]{2}\\d{2}" + bbanpattern + "$", "" ); 645 | if ( !( ibanregexp.test( iban ) ) ) { 646 | return false; // Invalid country specific format 647 | } 648 | } 649 | 650 | // Now check the checksum, first convert to digits 651 | ibancheck = iban.substring( 4, iban.length ) + iban.substring( 0, 4 ); 652 | for ( i = 0; i < ibancheck.length; i++ ) { 653 | charAt = ibancheck.charAt( i ); 654 | if ( charAt !== "0" ) { 655 | leadingZeroes = false; 656 | } 657 | if ( !leadingZeroes ) { 658 | ibancheckdigits += "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf( charAt ); 659 | } 660 | } 661 | 662 | // Calculate the result of: ibancheckdigits % 97 663 | for ( p = 0; p < ibancheckdigits.length; p++ ) { 664 | cChar = ibancheckdigits.charAt( p ); 665 | cOperator = "" + cRest + "" + cChar; 666 | cRest = cOperator % 97; 667 | } 668 | return cRest === 1; 669 | }, "Please specify a valid IBAN" ); 670 | 671 | $.validator.addMethod( "integer", function( value, element ) { 672 | return this.optional( element ) || /^-?\d+$/.test( value ); 673 | }, "A positive or negative non-decimal number please" ); 674 | 675 | $.validator.addMethod( "ipv4", function( value, element ) { 676 | return this.optional( element ) || /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i.test( value ); 677 | }, "Please enter a valid IP v4 address." ); 678 | 679 | $.validator.addMethod( "ipv6", function( value, element ) { 680 | return this.optional( element ) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test( value ); 681 | }, "Please enter a valid IP v6 address." ); 682 | 683 | $.validator.addMethod( "lettersonly", function( value, element ) { 684 | return this.optional( element ) || /^[a-z]+$/i.test( value ); 685 | }, "Letters only please" ); 686 | 687 | $.validator.addMethod( "letterswithbasicpunc", function( value, element ) { 688 | return this.optional( element ) || /^[a-z\-.,()'"\s]+$/i.test( value ); 689 | }, "Letters or punctuation only please" ); 690 | 691 | $.validator.addMethod( "mobileNL", function( value, element ) { 692 | return this.optional( element ) || /^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)6((\s|\s?\-\s?)?[0-9]){8}$/.test( value ); 693 | }, "Please specify a valid mobile number" ); 694 | 695 | /* For UK phone functions, do the following server side processing: 696 | * Compare original input with this RegEx pattern: 697 | * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$ 698 | * Extract $1 and set $prefix to '+44' if $1 is '44', otherwise set $prefix to '0' 699 | * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2. 700 | * A number of very detailed GB telephone number RegEx patterns can also be found at: 701 | * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers 702 | */ 703 | $.validator.addMethod( "mobileUK", function( phone_number, element ) { 704 | phone_number = phone_number.replace( /\(|\)|\s+|-/g, "" ); 705 | return this.optional( element ) || phone_number.length > 9 && 706 | phone_number.match( /^(?:(?:(?:00\s?|\+)44\s?|0)7(?:[1345789]\d{2}|624)\s?\d{3}\s?\d{3})$/ ); 707 | }, "Please specify a valid mobile number" ); 708 | 709 | $.validator.addMethod( "netmask", function( value, element ) { 710 | return this.optional( element ) || /^(254|252|248|240|224|192|128)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)/i.test( value ); 711 | }, "Please enter a valid netmask." ); 712 | 713 | /* 714 | * The NIE (Número de Identificación de Extranjero) is a Spanish tax identification number assigned by the Spanish 715 | * authorities to any foreigner. 716 | * 717 | * The NIE is the equivalent of a Spaniards Número de Identificación Fiscal (NIF) which serves as a fiscal 718 | * identification number. The CIF number (Certificado de Identificación Fiscal) is equivalent to the NIF, but applies to 719 | * companies rather than individuals. The NIE consists of an 'X' or 'Y' followed by 7 or 8 digits then another letter. 720 | */ 721 | $.validator.addMethod( "nieES", function( value, element ) { 722 | "use strict"; 723 | 724 | if ( this.optional( element ) ) { 725 | return true; 726 | } 727 | 728 | var nieRegEx = new RegExp( /^[MXYZ]{1}[0-9]{7,8}[TRWAGMYFPDXBNJZSQVHLCKET]{1}$/gi ); 729 | var validChars = "TRWAGMYFPDXBNJZSQVHLCKET", 730 | letter = value.substr( value.length - 1 ).toUpperCase(), 731 | number; 732 | 733 | value = value.toString().toUpperCase(); 734 | 735 | // Quick format test 736 | if ( value.length > 10 || value.length < 9 || !nieRegEx.test( value ) ) { 737 | return false; 738 | } 739 | 740 | // X means same number 741 | // Y means number + 10000000 742 | // Z means number + 20000000 743 | value = value.replace( /^[X]/, "0" ) 744 | .replace( /^[Y]/, "1" ) 745 | .replace( /^[Z]/, "2" ); 746 | 747 | number = value.length === 9 ? value.substr( 0, 8 ) : value.substr( 0, 9 ); 748 | 749 | return validChars.charAt( parseInt( number, 10 ) % 23 ) === letter; 750 | 751 | }, "Please specify a valid NIE number." ); 752 | 753 | /* 754 | * The Número de Identificación Fiscal ( NIF ) is the way tax identification used in Spain for individuals 755 | */ 756 | $.validator.addMethod( "nifES", function( value, element ) { 757 | "use strict"; 758 | 759 | if ( this.optional( element ) ) { 760 | return true; 761 | } 762 | 763 | value = value.toUpperCase(); 764 | 765 | // Basic format test 766 | if ( !value.match( "((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)" ) ) { 767 | return false; 768 | } 769 | 770 | // Test NIF 771 | if ( /^[0-9]{8}[A-Z]{1}$/.test( value ) ) { 772 | return ( "TRWAGMYFPDXBNJZSQVHLCKE".charAt( value.substring( 8, 0 ) % 23 ) === value.charAt( 8 ) ); 773 | } 774 | 775 | // Test specials NIF (starts with K, L or M) 776 | if ( /^[KLM]{1}/.test( value ) ) { 777 | return ( value[ 8 ] === "TRWAGMYFPDXBNJZSQVHLCKE".charAt( value.substring( 8, 1 ) % 23 ) ); 778 | } 779 | 780 | return false; 781 | 782 | }, "Please specify a valid NIF number." ); 783 | 784 | /* 785 | * Numer identyfikacji podatkowej ( NIP ) is the way tax identification used in Poland for companies 786 | */ 787 | $.validator.addMethod( "nipPL", function( value ) { 788 | "use strict"; 789 | 790 | value = value.replace( /[^0-9]/g, "" ); 791 | 792 | if ( value.length !== 10 ) { 793 | return false; 794 | } 795 | 796 | var arrSteps = [ 6, 5, 7, 2, 3, 4, 5, 6, 7 ]; 797 | var intSum = 0; 798 | for ( var i = 0; i < 9; i++ ) { 799 | intSum += arrSteps[ i ] * value[ i ]; 800 | } 801 | var int2 = intSum % 11; 802 | var intControlNr = ( int2 === 10 ) ? 0 : int2; 803 | 804 | return ( intControlNr === parseInt( value[ 9 ], 10 ) ); 805 | }, "Please specify a valid NIP number." ); 806 | 807 | $.validator.addMethod( "notEqualTo", function( value, element, param ) { 808 | return this.optional( element ) || !$.validator.methods.equalTo.call( this, value, element, param ); 809 | }, "Please enter a different value, values must not be the same." ); 810 | 811 | $.validator.addMethod( "nowhitespace", function( value, element ) { 812 | return this.optional( element ) || /^\S+$/i.test( value ); 813 | }, "No white space please" ); 814 | 815 | /** 816 | * Return true if the field value matches the given format RegExp 817 | * 818 | * @example $.validator.methods.pattern("AR1004",element,/^AR\d{4}$/) 819 | * @result true 820 | * 821 | * @example $.validator.methods.pattern("BR1004",element,/^AR\d{4}$/) 822 | * @result false 823 | * 824 | * @name $.validator.methods.pattern 825 | * @type Boolean 826 | * @cat Plugins/Validate/Methods 827 | */ 828 | $.validator.addMethod( "pattern", function( value, element, param ) { 829 | if ( this.optional( element ) ) { 830 | return true; 831 | } 832 | if ( typeof param === "string" ) { 833 | param = new RegExp( "^(?:" + param + ")$" ); 834 | } 835 | return param.test( value ); 836 | }, "Invalid format." ); 837 | 838 | /** 839 | * Dutch phone numbers have 10 digits (or 11 and start with +31). 840 | */ 841 | $.validator.addMethod( "phoneNL", function( value, element ) { 842 | return this.optional( element ) || /^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)[1-9]((\s|\s?\-\s?)?[0-9]){8}$/.test( value ); 843 | }, "Please specify a valid phone number." ); 844 | 845 | /* For UK phone functions, do the following server side processing: 846 | * Compare original input with this RegEx pattern: 847 | * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$ 848 | * Extract $1 and set $prefix to '+44' if $1 is '44', otherwise set $prefix to '0' 849 | * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2. 850 | * A number of very detailed GB telephone number RegEx patterns can also be found at: 851 | * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers 852 | */ 853 | 854 | // Matches UK landline + mobile, accepting only 01-3 for landline or 07 for mobile to exclude many premium numbers 855 | $.validator.addMethod( "phonesUK", function( phone_number, element ) { 856 | phone_number = phone_number.replace( /\(|\)|\s+|-/g, "" ); 857 | return this.optional( element ) || phone_number.length > 9 && 858 | phone_number.match( /^(?:(?:(?:00\s?|\+)44\s?|0)(?:1\d{8,9}|[23]\d{9}|7(?:[1345789]\d{8}|624\d{6})))$/ ); 859 | }, "Please specify a valid uk phone number" ); 860 | 861 | /* For UK phone functions, do the following server side processing: 862 | * Compare original input with this RegEx pattern: 863 | * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$ 864 | * Extract $1 and set $prefix to '+44' if $1 is '44', otherwise set $prefix to '0' 865 | * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2. 866 | * A number of very detailed GB telephone number RegEx patterns can also be found at: 867 | * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers 868 | */ 869 | $.validator.addMethod( "phoneUK", function( phone_number, element ) { 870 | phone_number = phone_number.replace( /\(|\)|\s+|-/g, "" ); 871 | return this.optional( element ) || phone_number.length > 9 && 872 | phone_number.match( /^(?:(?:(?:00\s?|\+)44\s?)|(?:\(?0))(?:\d{2}\)?\s?\d{4}\s?\d{4}|\d{3}\)?\s?\d{3}\s?\d{3,4}|\d{4}\)?\s?(?:\d{5}|\d{3}\s?\d{3})|\d{5}\)?\s?\d{4,5})$/ ); 873 | }, "Please specify a valid phone number" ); 874 | 875 | /** 876 | * Matches US phone number format 877 | * 878 | * where the area code may not start with 1 and the prefix may not start with 1 879 | * allows '-' or ' ' as a separator and allows parens around area code 880 | * some people may want to put a '1' in front of their number 881 | * 882 | * 1(212)-999-2345 or 883 | * 212 999 2344 or 884 | * 212-999-0983 885 | * 886 | * but not 887 | * 111-123-5434 888 | * and not 889 | * 212 123 4567 890 | */ 891 | $.validator.addMethod( "phoneUS", function( phone_number, element ) { 892 | phone_number = phone_number.replace( /\s+/g, "" ); 893 | return this.optional( element ) || phone_number.length > 9 && 894 | phone_number.match( /^(\+?1-?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?[2-9]([02-9]\d|1[02-9])-?\d{4}$/ ); 895 | }, "Please specify a valid phone number" ); 896 | 897 | /* 898 | * Valida CEPs do brasileiros: 899 | * 900 | * Formatos aceitos: 901 | * 99999-999 902 | * 99.999-999 903 | * 99999999 904 | */ 905 | $.validator.addMethod( "postalcodeBR", function( cep_value, element ) { 906 | return this.optional( element ) || /^\d{2}.\d{3}-\d{3}?$|^\d{5}-?\d{3}?$/.test( cep_value ); 907 | }, "Informe um CEP válido." ); 908 | 909 | /** 910 | * Matches a valid Canadian Postal Code 911 | * 912 | * @example jQuery.validator.methods.postalCodeCA( "H0H 0H0", element ) 913 | * @result true 914 | * 915 | * @example jQuery.validator.methods.postalCodeCA( "H0H0H0", element ) 916 | * @result false 917 | * 918 | * @name jQuery.validator.methods.postalCodeCA 919 | * @type Boolean 920 | * @cat Plugins/Validate/Methods 921 | */ 922 | $.validator.addMethod( "postalCodeCA", function( value, element ) { 923 | return this.optional( element ) || /^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ] *\d[ABCEGHJKLMNPRSTVWXYZ]\d$/i.test( value ); 924 | }, "Please specify a valid postal code" ); 925 | 926 | /* Matches Italian postcode (CAP) */ 927 | $.validator.addMethod( "postalcodeIT", function( value, element ) { 928 | return this.optional( element ) || /^\d{5}$/.test( value ); 929 | }, "Please specify a valid postal code" ); 930 | 931 | $.validator.addMethod( "postalcodeNL", function( value, element ) { 932 | return this.optional( element ) || /^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/.test( value ); 933 | }, "Please specify a valid postal code" ); 934 | 935 | // Matches UK postcode. Does not match to UK Channel Islands that have their own postcodes (non standard UK) 936 | $.validator.addMethod( "postcodeUK", function( value, element ) { 937 | return this.optional( element ) || /^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([A-PR-UWYZ][A-HK-Y][0-9][0-9])|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]))\s?([0-9][ABD-HJLNP-UW-Z]{2})|(GIR)\s?(0AA))$/i.test( value ); 938 | }, "Please specify a valid UK postcode" ); 939 | 940 | /* 941 | * Lets you say "at least X inputs that match selector Y must be filled." 942 | * 943 | * The end result is that neither of these inputs: 944 | * 945 | * 946 | * 947 | * 948 | * ...will validate unless at least one of them is filled. 949 | * 950 | * partnumber: {require_from_group: [1,".productinfo"]}, 951 | * description: {require_from_group: [1,".productinfo"]} 952 | * 953 | * options[0]: number of fields that must be filled in the group 954 | * options[1]: CSS selector that defines the group of conditionally required fields 955 | */ 956 | $.validator.addMethod( "require_from_group", function( value, element, options ) { 957 | var $fields = $( options[ 1 ], element.form ), 958 | $fieldsFirst = $fields.eq( 0 ), 959 | validator = $fieldsFirst.data( "valid_req_grp" ) ? $fieldsFirst.data( "valid_req_grp" ) : $.extend( {}, this ), 960 | isValid = $fields.filter( function() { 961 | return validator.elementValue( this ); 962 | } ).length >= options[ 0 ]; 963 | 964 | // Store the cloned validator for future validation 965 | $fieldsFirst.data( "valid_req_grp", validator ); 966 | 967 | // If element isn't being validated, run each require_from_group field's validation rules 968 | if ( !$( element ).data( "being_validated" ) ) { 969 | $fields.data( "being_validated", true ); 970 | $fields.each( function() { 971 | validator.element( this ); 972 | } ); 973 | $fields.data( "being_validated", false ); 974 | } 975 | return isValid; 976 | }, $.validator.format( "Please fill at least {0} of these fields." ) ); 977 | 978 | /* 979 | * Lets you say "either at least X inputs that match selector Y must be filled, 980 | * OR they must all be skipped (left blank)." 981 | * 982 | * The end result, is that none of these inputs: 983 | * 984 | * 985 | * 986 | * 987 | * 988 | * ...will validate unless either at least two of them are filled, 989 | * OR none of them are. 990 | * 991 | * partnumber: {skip_or_fill_minimum: [2,".productinfo"]}, 992 | * description: {skip_or_fill_minimum: [2,".productinfo"]}, 993 | * color: {skip_or_fill_minimum: [2,".productinfo"]} 994 | * 995 | * options[0]: number of fields that must be filled in the group 996 | * options[1]: CSS selector that defines the group of conditionally required fields 997 | * 998 | */ 999 | $.validator.addMethod( "skip_or_fill_minimum", function( value, element, options ) { 1000 | var $fields = $( options[ 1 ], element.form ), 1001 | $fieldsFirst = $fields.eq( 0 ), 1002 | validator = $fieldsFirst.data( "valid_skip" ) ? $fieldsFirst.data( "valid_skip" ) : $.extend( {}, this ), 1003 | numberFilled = $fields.filter( function() { 1004 | return validator.elementValue( this ); 1005 | } ).length, 1006 | isValid = numberFilled === 0 || numberFilled >= options[ 0 ]; 1007 | 1008 | // Store the cloned validator for future validation 1009 | $fieldsFirst.data( "valid_skip", validator ); 1010 | 1011 | // If element isn't being validated, run each skip_or_fill_minimum field's validation rules 1012 | if ( !$( element ).data( "being_validated" ) ) { 1013 | $fields.data( "being_validated", true ); 1014 | $fields.each( function() { 1015 | validator.element( this ); 1016 | } ); 1017 | $fields.data( "being_validated", false ); 1018 | } 1019 | return isValid; 1020 | }, $.validator.format( "Please either skip these fields or fill at least {0} of them." ) ); 1021 | 1022 | /* Validates US States and/or Territories by @jdforsythe 1023 | * Can be case insensitive or require capitalization - default is case insensitive 1024 | * Can include US Territories or not - default does not 1025 | * Can include US Military postal abbreviations (AA, AE, AP) - default does not 1026 | * 1027 | * Note: "States" always includes DC (District of Colombia) 1028 | * 1029 | * Usage examples: 1030 | * 1031 | * This is the default - case insensitive, no territories, no military zones 1032 | * stateInput: { 1033 | * caseSensitive: false, 1034 | * includeTerritories: false, 1035 | * includeMilitary: false 1036 | * } 1037 | * 1038 | * Only allow capital letters, no territories, no military zones 1039 | * stateInput: { 1040 | * caseSensitive: false 1041 | * } 1042 | * 1043 | * Case insensitive, include territories but not military zones 1044 | * stateInput: { 1045 | * includeTerritories: true 1046 | * } 1047 | * 1048 | * Only allow capital letters, include territories and military zones 1049 | * stateInput: { 1050 | * caseSensitive: true, 1051 | * includeTerritories: true, 1052 | * includeMilitary: true 1053 | * } 1054 | * 1055 | */ 1056 | $.validator.addMethod( "stateUS", function( value, element, options ) { 1057 | var isDefault = typeof options === "undefined", 1058 | caseSensitive = ( isDefault || typeof options.caseSensitive === "undefined" ) ? false : options.caseSensitive, 1059 | includeTerritories = ( isDefault || typeof options.includeTerritories === "undefined" ) ? false : options.includeTerritories, 1060 | includeMilitary = ( isDefault || typeof options.includeMilitary === "undefined" ) ? false : options.includeMilitary, 1061 | regex; 1062 | 1063 | if ( !includeTerritories && !includeMilitary ) { 1064 | regex = "^(A[KLRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$"; 1065 | } else if ( includeTerritories && includeMilitary ) { 1066 | regex = "^(A[AEKLPRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$"; 1067 | } else if ( includeTerritories ) { 1068 | regex = "^(A[KLRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$"; 1069 | } else { 1070 | regex = "^(A[AEKLPRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$"; 1071 | } 1072 | 1073 | regex = caseSensitive ? new RegExp( regex ) : new RegExp( regex, "i" ); 1074 | return this.optional( element ) || regex.test( value ); 1075 | }, "Please specify a valid state" ); 1076 | 1077 | // TODO check if value starts with <, otherwise don't try stripping anything 1078 | $.validator.addMethod( "strippedminlength", function( value, element, param ) { 1079 | return $( value ).text().length >= param; 1080 | }, $.validator.format( "Please enter at least {0} characters" ) ); 1081 | 1082 | $.validator.addMethod( "time", function( value, element ) { 1083 | return this.optional( element ) || /^([01]\d|2[0-3]|[0-9])(:[0-5]\d){1,2}$/.test( value ); 1084 | }, "Please enter a valid time, between 00:00 and 23:59" ); 1085 | 1086 | $.validator.addMethod( "time12h", function( value, element ) { 1087 | return this.optional( element ) || /^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test( value ); 1088 | }, "Please enter a valid time in 12-hour am/pm format" ); 1089 | 1090 | // Same as url, but TLD is optional 1091 | $.validator.addMethod( "url2", function( value, element ) { 1092 | return this.optional( element ) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test( value ); 1093 | }, $.validator.messages.url ); 1094 | 1095 | /** 1096 | * Return true, if the value is a valid vehicle identification number (VIN). 1097 | * 1098 | * Works with all kind of text inputs. 1099 | * 1100 | * @example 1101 | * @desc Declares a required input element whose value must be a valid vehicle identification number. 1102 | * 1103 | * @name $.validator.methods.vinUS 1104 | * @type Boolean 1105 | * @cat Plugins/Validate/Methods 1106 | */ 1107 | $.validator.addMethod( "vinUS", function( v ) { 1108 | if ( v.length !== 17 ) { 1109 | return false; 1110 | } 1111 | 1112 | var LL = [ "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" ], 1113 | VL = [ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 7, 9, 2, 3, 4, 5, 6, 7, 8, 9 ], 1114 | FL = [ 8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2 ], 1115 | rs = 0, 1116 | i, n, d, f, cd, cdv; 1117 | 1118 | for ( i = 0; i < 17; i++ ) { 1119 | f = FL[ i ]; 1120 | d = v.slice( i, i + 1 ); 1121 | if ( i === 8 ) { 1122 | cdv = d; 1123 | } 1124 | if ( !isNaN( d ) ) { 1125 | d *= f; 1126 | } else { 1127 | for ( n = 0; n < LL.length; n++ ) { 1128 | if ( d.toUpperCase() === LL[ n ] ) { 1129 | d = VL[ n ]; 1130 | d *= f; 1131 | if ( isNaN( cdv ) && n === 8 ) { 1132 | cdv = LL[ n ]; 1133 | } 1134 | break; 1135 | } 1136 | } 1137 | } 1138 | rs += d; 1139 | } 1140 | cd = rs % 11; 1141 | if ( cd === 10 ) { 1142 | cd = "X"; 1143 | } 1144 | if ( cd === cdv ) { 1145 | return true; 1146 | } 1147 | return false; 1148 | }, "The specified vehicle identification number (VIN) is invalid." ); 1149 | 1150 | $.validator.addMethod( "zipcodeUS", function( value, element ) { 1151 | return this.optional( element ) || /^\d{5}(-\d{4})?$/.test( value ); 1152 | }, "The specified US ZIP Code is invalid" ); 1153 | 1154 | $.validator.addMethod( "ziprange", function( value, element ) { 1155 | return this.optional( element ) || /^90[2-5]\d\{2\}-\d{4}$/.test( value ); 1156 | }, "Your ZIP-code must be in the range 902xx-xxxx to 905xx-xxxx" ); 1157 | return $; 1158 | })); -------------------------------------------------------------------------------- /BuildMonitor/wwwroot/lib/jquery-validation/dist/additional-methods.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery Validation Plugin - v1.17.0 - 7/29/2017 2 | * https://jqueryvalidation.org/ 3 | * Copyright (c) 2017 Jörn Zaefferer; Licensed MIT */ 4 | !function(a){"function"==typeof define&&define.amd?define(["jquery","./jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return function(){function b(a){return a.replace(/<.[^<>]*?>/g," ").replace(/ | /gi," ").replace(/[.(),;:!?%#$'\"_+=\/\-“”’]*/g,"")}a.validator.addMethod("maxWords",function(a,c,d){return this.optional(c)||b(a).match(/\b\w+\b/g).length<=d},a.validator.format("Please enter {0} words or less.")),a.validator.addMethod("minWords",function(a,c,d){return this.optional(c)||b(a).match(/\b\w+\b/g).length>=d},a.validator.format("Please enter at least {0} words.")),a.validator.addMethod("rangeWords",function(a,c,d){var e=b(a),f=/\b\w+\b/g;return this.optional(c)||e.match(f).length>=d[0]&&e.match(f).length<=d[1]},a.validator.format("Please enter between {0} and {1} words."))}(),a.validator.addMethod("accept",function(b,c,d){var e,f,g,h="string"==typeof d?d.replace(/\s/g,""):"image/*",i=this.optional(c);if(i)return i;if("file"===a(c).attr("type")&&(h=h.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g,"\\$&").replace(/,/g,"|").replace(/\/\*/g,"/.*"),c.files&&c.files.length))for(g=new RegExp(".?("+h+")$","i"),e=0;e9?"0":f,g="JABCDEFGHI".substr(f,1).toString(),i.match(/[ABEH]/)?k===f:i.match(/[KPQS]/)?k===g:k===f||k===g},"Please specify a valid CIF number."),a.validator.addMethod("cpfBR",function(a){if(a=a.replace(/([~!@#$%^&*()_+=`{}\[\]\-|\\:;'<>,.\/? ])+/g,""),11!==a.length)return!1;var b,c,d,e,f=0;if(b=parseInt(a.substring(9,10),10),c=parseInt(a.substring(10,11),10),d=function(a,b){var c=10*a%11;return 10!==c&&11!==c||(c=0),c===b},""===a||"00000000000"===a||"11111111111"===a||"22222222222"===a||"33333333333"===a||"44444444444"===a||"55555555555"===a||"66666666666"===a||"77777777777"===a||"88888888888"===a||"99999999999"===a)return!1;for(e=1;e<=9;e++)f+=parseInt(a.substring(e-1,e),10)*(11-e);if(d(f,b)){for(f=0,e=1;e<=10;e++)f+=parseInt(a.substring(e-1,e),10)*(12-e);return d(f,c)}return!1},"Please specify a valid CPF number"),a.validator.addMethod("creditcard",function(a,b){if(this.optional(b))return"dependency-mismatch";if(/[^0-9 \-]+/.test(a))return!1;var c,d,e=0,f=0,g=!1;if(a=a.replace(/\D/g,""),a.length<13||a.length>19)return!1;for(c=a.length-1;c>=0;c--)d=a.charAt(c),f=parseInt(d,10),g&&(f*=2)>9&&(f-=9),e+=f,g=!g;return e%10===0},"Please enter a valid credit card number."),a.validator.addMethod("creditcardtypes",function(a,b,c){if(/[^0-9\-]+/.test(a))return!1;a=a.replace(/\D/g,"");var d=0;return c.mastercard&&(d|=1),c.visa&&(d|=2),c.amex&&(d|=4),c.dinersclub&&(d|=8),c.enroute&&(d|=16),c.discover&&(d|=32),c.jcb&&(d|=64),c.unknown&&(d|=128),c.all&&(d=255),1&d&&/^(5[12345])/.test(a)?16===a.length:2&d&&/^(4)/.test(a)?16===a.length:4&d&&/^(3[47])/.test(a)?15===a.length:8&d&&/^(3(0[012345]|[68]))/.test(a)?14===a.length:16&d&&/^(2(014|149))/.test(a)?15===a.length:32&d&&/^(6011)/.test(a)?16===a.length:64&d&&/^(3)/.test(a)?16===a.length:64&d&&/^(2131|1800)/.test(a)?15===a.length:!!(128&d)},"Please enter a valid credit card number."),a.validator.addMethod("currency",function(a,b,c){var d,e="string"==typeof c,f=e?c:c[0],g=!!e||c[1];return f=f.replace(/,/g,""),f=g?f+"]":f+"]?",d="^["+f+"([1-9]{1}[0-9]{0,2}(\\,[0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)$",d=new RegExp(d),this.optional(b)||d.test(a)},"Please specify a valid currency"),a.validator.addMethod("dateFA",function(a,b){return this.optional(b)||/^[1-4]\d{3}\/((0?[1-6]\/((3[0-1])|([1-2][0-9])|(0?[1-9])))|((1[0-2]|(0?[7-9]))\/(30|([1-2][0-9])|(0?[1-9]))))$/.test(a)},a.validator.messages.date),a.validator.addMethod("dateITA",function(a,b){var c,d,e,f,g,h=!1,i=/^\d{1,2}\/\d{1,2}\/\d{4}$/;return i.test(a)?(c=a.split("/"),d=parseInt(c[0],10),e=parseInt(c[1],10),f=parseInt(c[2],10),g=new Date(Date.UTC(f,e-1,d,12,0,0,0)),h=g.getUTCFullYear()===f&&g.getUTCMonth()===e-1&&g.getUTCDate()===d):h=!1,this.optional(b)||h},a.validator.messages.date),a.validator.addMethod("dateNL",function(a,b){return this.optional(b)||/^(0?[1-9]|[12]\d|3[01])[\.\/\-](0?[1-9]|1[012])[\.\/\-]([12]\d)?(\d\d)$/.test(a)},a.validator.messages.date),a.validator.addMethod("extension",function(a,b,c){return c="string"==typeof c?c.replace(/,/g,"|"):"png|jpe?g|gif",this.optional(b)||a.match(new RegExp("\\.("+c+")$","i"))},a.validator.format("Please enter a value with a valid extension.")),a.validator.addMethod("giroaccountNL",function(a,b){return this.optional(b)||/^[0-9]{1,7}$/.test(a)},"Please specify a valid giro account number"),a.validator.addMethod("iban",function(a,b){if(this.optional(b))return!0;var c,d,e,f,g,h,i,j,k,l=a.replace(/ /g,"").toUpperCase(),m="",n=!0,o="",p="",q=5;if(l.length9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?|0)7(?:[1345789]\d{2}|624)\s?\d{3}\s?\d{3})$/)},"Please specify a valid mobile number"),a.validator.addMethod("netmask",function(a,b){return this.optional(b)||/^(254|252|248|240|224|192|128)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)/i.test(a)},"Please enter a valid netmask."),a.validator.addMethod("nieES",function(a,b){"use strict";if(this.optional(b))return!0;var c,d=new RegExp(/^[MXYZ]{1}[0-9]{7,8}[TRWAGMYFPDXBNJZSQVHLCKET]{1}$/gi),e="TRWAGMYFPDXBNJZSQVHLCKET",f=a.substr(a.length-1).toUpperCase();return a=a.toString().toUpperCase(),!(a.length>10||a.length<9||!d.test(a))&&(a=a.replace(/^[X]/,"0").replace(/^[Y]/,"1").replace(/^[Z]/,"2"),c=9===a.length?a.substr(0,8):a.substr(0,9),e.charAt(parseInt(c,10)%23)===f)},"Please specify a valid NIE number."),a.validator.addMethod("nifES",function(a,b){"use strict";return!!this.optional(b)||(a=a.toUpperCase(),!!a.match("((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)")&&(/^[0-9]{8}[A-Z]{1}$/.test(a)?"TRWAGMYFPDXBNJZSQVHLCKE".charAt(a.substring(8,0)%23)===a.charAt(8):!!/^[KLM]{1}/.test(a)&&a[8]==="TRWAGMYFPDXBNJZSQVHLCKE".charAt(a.substring(8,1)%23)))},"Please specify a valid NIF number."),a.validator.addMethod("nipPL",function(a){"use strict";if(a=a.replace(/[^0-9]/g,""),10!==a.length)return!1;for(var b=[6,5,7,2,3,4,5,6,7],c=0,d=0;d<9;d++)c+=b[d]*a[d];var e=c%11,f=10===e?0:e;return f===parseInt(a[9],10)},"Please specify a valid NIP number."),a.validator.addMethod("notEqualTo",function(b,c,d){return this.optional(c)||!a.validator.methods.equalTo.call(this,b,c,d)},"Please enter a different value, values must not be the same."),a.validator.addMethod("nowhitespace",function(a,b){return this.optional(b)||/^\S+$/i.test(a)},"No white space please"),a.validator.addMethod("pattern",function(a,b,c){return!!this.optional(b)||("string"==typeof c&&(c=new RegExp("^(?:"+c+")$")),c.test(a))},"Invalid format."),a.validator.addMethod("phoneNL",function(a,b){return this.optional(b)||/^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)[1-9]((\s|\s?\-\s?)?[0-9]){8}$/.test(a)},"Please specify a valid phone number."),a.validator.addMethod("phonesUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?|0)(?:1\d{8,9}|[23]\d{9}|7(?:[1345789]\d{8}|624\d{6})))$/)},"Please specify a valid uk phone number"),a.validator.addMethod("phoneUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?)|(?:\(?0))(?:\d{2}\)?\s?\d{4}\s?\d{4}|\d{3}\)?\s?\d{3}\s?\d{3,4}|\d{4}\)?\s?(?:\d{5}|\d{3}\s?\d{3})|\d{5}\)?\s?\d{4,5})$/)},"Please specify a valid phone number"),a.validator.addMethod("phoneUS",function(a,b){return a=a.replace(/\s+/g,""),this.optional(b)||a.length>9&&a.match(/^(\+?1-?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?[2-9]([02-9]\d|1[02-9])-?\d{4}$/)},"Please specify a valid phone number"),a.validator.addMethod("postalcodeBR",function(a,b){return this.optional(b)||/^\d{2}.\d{3}-\d{3}?$|^\d{5}-?\d{3}?$/.test(a)},"Informe um CEP válido."),a.validator.addMethod("postalCodeCA",function(a,b){return this.optional(b)||/^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ] *\d[ABCEGHJKLMNPRSTVWXYZ]\d$/i.test(a)},"Please specify a valid postal code"),a.validator.addMethod("postalcodeIT",function(a,b){return this.optional(b)||/^\d{5}$/.test(a)},"Please specify a valid postal code"),a.validator.addMethod("postalcodeNL",function(a,b){return this.optional(b)||/^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/.test(a)},"Please specify a valid postal code"),a.validator.addMethod("postcodeUK",function(a,b){return this.optional(b)||/^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([A-PR-UWYZ][A-HK-Y][0-9][0-9])|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]))\s?([0-9][ABD-HJLNP-UW-Z]{2})|(GIR)\s?(0AA))$/i.test(a)},"Please specify a valid UK postcode"),a.validator.addMethod("require_from_group",function(b,c,d){var e=a(d[1],c.form),f=e.eq(0),g=f.data("valid_req_grp")?f.data("valid_req_grp"):a.extend({},this),h=e.filter(function(){return g.elementValue(this)}).length>=d[0];return f.data("valid_req_grp",g),a(c).data("being_validated")||(e.data("being_validated",!0),e.each(function(){g.element(this)}),e.data("being_validated",!1)),h},a.validator.format("Please fill at least {0} of these fields.")),a.validator.addMethod("skip_or_fill_minimum",function(b,c,d){var e=a(d[1],c.form),f=e.eq(0),g=f.data("valid_skip")?f.data("valid_skip"):a.extend({},this),h=e.filter(function(){return g.elementValue(this)}).length,i=0===h||h>=d[0];return f.data("valid_skip",g),a(c).data("being_validated")||(e.data("being_validated",!0),e.each(function(){g.element(this)}),e.data("being_validated",!1)),i},a.validator.format("Please either skip these fields or fill at least {0} of them.")),a.validator.addMethod("stateUS",function(a,b,c){var d,e="undefined"==typeof c,f=!e&&"undefined"!=typeof c.caseSensitive&&c.caseSensitive,g=!e&&"undefined"!=typeof c.includeTerritories&&c.includeTerritories,h=!e&&"undefined"!=typeof c.includeMilitary&&c.includeMilitary;return d=g||h?g&&h?"^(A[AEKLPRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$":g?"^(A[KLRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$":"^(A[AEKLPRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$":"^(A[KLRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$",d=f?new RegExp(d):new RegExp(d,"i"),this.optional(b)||d.test(a)},"Please specify a valid state"),a.validator.addMethod("strippedminlength",function(b,c,d){return a(b).text().length>=d},a.validator.format("Please enter at least {0} characters")),a.validator.addMethod("time",function(a,b){return this.optional(b)||/^([01]\d|2[0-3]|[0-9])(:[0-5]\d){1,2}$/.test(a)},"Please enter a valid time, between 00:00 and 23:59"),a.validator.addMethod("time12h",function(a,b){return this.optional(b)||/^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test(a)},"Please enter a valid time in 12-hour am/pm format"),a.validator.addMethod("url2",function(a,b){return this.optional(b)||/^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(a)},a.validator.messages.url),a.validator.addMethod("vinUS",function(a){if(17!==a.length)return!1;var b,c,d,e,f,g,h=["A","B","C","D","E","F","G","H","J","K","L","M","N","P","R","S","T","U","V","W","X","Y","Z"],i=[1,2,3,4,5,6,7,8,1,2,3,4,5,7,9,2,3,4,5,6,7,8,9],j=[8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2],k=0;for(b=0;b<17;b++){if(e=j[b],d=a.slice(b,b+1),8===b&&(g=d),isNaN(d)){for(c=0;c").attr("name",c.submitButton.name).val(a(c.submitButton).val()).appendTo(c.currentForm)),!c.settings.submitHandler||(e=c.settings.submitHandler.call(c,c.currentForm,b),d&&d.remove(),void 0!==e&&e)}return c.settings.debug&&b.preventDefault(),c.cancelSubmit?(c.cancelSubmit=!1,d()):c.form()?c.pendingRequest?(c.formSubmitted=!0,!1):d():(c.focusInvalid(),!1)})),c)},valid:function(){var b,c,d;return a(this[0]).is("form")?b=this.validate().form():(d=[],b=!0,c=a(this[0].form).validate(),this.each(function(){b=c.element(this)&&b,b||(d=d.concat(c.errorList))}),c.errorList=d),b},rules:function(b,c){var d,e,f,g,h,i,j=this[0];if(null!=j&&(!j.form&&j.hasAttribute("contenteditable")&&(j.form=this.closest("form")[0],j.name=this.attr("name")),null!=j.form)){if(b)switch(d=a.data(j.form,"validator").settings,e=d.rules,f=a.validator.staticRules(j),b){case"add":a.extend(f,a.validator.normalizeRule(c)),delete f.messages,e[j.name]=f,c.messages&&(d.messages[j.name]=a.extend(d.messages[j.name],c.messages));break;case"remove":return c?(i={},a.each(c.split(/\s/),function(a,b){i[b]=f[b],delete f[b]}),i):(delete e[j.name],f)}return g=a.validator.normalizeRules(a.extend({},a.validator.classRules(j),a.validator.attributeRules(j),a.validator.dataRules(j),a.validator.staticRules(j)),j),g.required&&(h=g.required,delete g.required,g=a.extend({required:h},g)),g.remote&&(h=g.remote,delete g.remote,g=a.extend(g,{remote:h})),g}}}),a.extend(a.expr.pseudos||a.expr[":"],{blank:function(b){return!a.trim(""+a(b).val())},filled:function(b){var c=a(b).val();return null!==c&&!!a.trim(""+c)},unchecked:function(b){return!a(b).prop("checked")}}),a.validator=function(b,c){this.settings=a.extend(!0,{},a.validator.defaults,b),this.currentForm=c,this.init()},a.validator.format=function(b,c){return 1===arguments.length?function(){var c=a.makeArray(arguments);return c.unshift(b),a.validator.format.apply(this,c)}:void 0===c?b:(arguments.length>2&&c.constructor!==Array&&(c=a.makeArray(arguments).slice(1)),c.constructor!==Array&&(c=[c]),a.each(c,function(a,c){b=b.replace(new RegExp("\\{"+a+"\\}","g"),function(){return c})}),b)},a.extend(a.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",pendingClass:"pending",validClass:"valid",errorElement:"label",focusCleanup:!1,focusInvalid:!0,errorContainer:a([]),errorLabelContainer:a([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(a){this.lastActive=a,this.settings.focusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,a,this.settings.errorClass,this.settings.validClass),this.hideThese(this.errorsFor(a)))},onfocusout:function(a){this.checkable(a)||!(a.name in this.submitted)&&this.optional(a)||this.element(a)},onkeyup:function(b,c){var d=[16,17,18,20,35,36,37,38,39,40,45,144,225];9===c.which&&""===this.elementValue(b)||a.inArray(c.keyCode,d)!==-1||(b.name in this.submitted||b.name in this.invalid)&&this.element(b)},onclick:function(a){a.name in this.submitted?this.element(a):a.parentNode.name in this.submitted&&this.element(a.parentNode)},highlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).addClass(c).removeClass(d):a(b).addClass(c).removeClass(d)},unhighlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).removeClass(c).addClass(d):a(b).removeClass(c).addClass(d)}},setDefaults:function(b){a.extend(a.validator.defaults,b)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date (ISO).",number:"Please enter a valid number.",digits:"Please enter only digits.",equalTo:"Please enter the same value again.",maxlength:a.validator.format("Please enter no more than {0} characters."),minlength:a.validator.format("Please enter at least {0} characters."),rangelength:a.validator.format("Please enter a value between {0} and {1} characters long."),range:a.validator.format("Please enter a value between {0} and {1}."),max:a.validator.format("Please enter a value less than or equal to {0}."),min:a.validator.format("Please enter a value greater than or equal to {0}."),step:a.validator.format("Please enter a multiple of {0}.")},autoCreateRanges:!1,prototype:{init:function(){function b(b){!this.form&&this.hasAttribute("contenteditable")&&(this.form=a(this).closest("form")[0],this.name=a(this).attr("name"));var c=a.data(this.form,"validator"),d="on"+b.type.replace(/^validate/,""),e=c.settings;e[d]&&!a(this).is(e.ignore)&&e[d].call(c,this,b)}this.labelContainer=a(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||a(this.currentForm),this.containers=a(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var c,d=this.groups={};a.each(this.settings.groups,function(b,c){"string"==typeof c&&(c=c.split(/\s/)),a.each(c,function(a,c){d[c]=b})}),c=this.settings.rules,a.each(c,function(b,d){c[b]=a.validator.normalizeRule(d)}),a(this.currentForm).on("focusin.validate focusout.validate keyup.validate",":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], [type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], [type='radio'], [type='checkbox'], [contenteditable], [type='button']",b).on("click.validate","select, option, [type='radio'], [type='checkbox']",b),this.settings.invalidHandler&&a(this.currentForm).on("invalid-form.validate",this.settings.invalidHandler)},form:function(){return this.checkForm(),a.extend(this.submitted,this.errorMap),this.invalid=a.extend({},this.errorMap),this.valid()||a(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var a=0,b=this.currentElements=this.elements();b[a];a++)this.check(b[a]);return this.valid()},element:function(b){var c,d,e=this.clean(b),f=this.validationTargetFor(e),g=this,h=!0;return void 0===f?delete this.invalid[e.name]:(this.prepareElement(f),this.currentElements=a(f),d=this.groups[f.name],d&&a.each(this.groups,function(a,b){b===d&&a!==f.name&&(e=g.validationTargetFor(g.clean(g.findByName(a))),e&&e.name in g.invalid&&(g.currentElements.push(e),h=g.check(e)&&h))}),c=this.check(f)!==!1,h=h&&c,c?this.invalid[f.name]=!1:this.invalid[f.name]=!0,this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),a(b).attr("aria-invalid",!c)),h},showErrors:function(b){if(b){var c=this;a.extend(this.errorMap,b),this.errorList=a.map(this.errorMap,function(a,b){return{message:a,element:c.findByName(b)[0]}}),this.successList=a.grep(this.successList,function(a){return!(a.name in b)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){a.fn.resetForm&&a(this.currentForm).resetForm(),this.invalid={},this.submitted={},this.prepareForm(),this.hideErrors();var b=this.elements().removeData("previousValue").removeAttr("aria-invalid");this.resetElements(b)},resetElements:function(a){var b;if(this.settings.unhighlight)for(b=0;a[b];b++)this.settings.unhighlight.call(this,a[b],this.settings.errorClass,""),this.findByName(a[b].name).removeClass(this.settings.validClass);else a.removeClass(this.settings.errorClass).removeClass(this.settings.validClass)},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(a){var b,c=0;for(b in a)void 0!==a[b]&&null!==a[b]&&a[b]!==!1&&c++;return c},hideErrors:function(){this.hideThese(this.toHide)},hideThese:function(a){a.not(this.containers).text(""),this.addWrapper(a).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{a(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(b){}},findLastActive:function(){var b=this.lastActive;return b&&1===a.grep(this.errorList,function(a){return a.element.name===b.name}).length&&b},elements:function(){var b=this,c={};return a(this.currentForm).find("input, select, textarea, [contenteditable]").not(":submit, :reset, :image, :disabled").not(this.settings.ignore).filter(function(){var d=this.name||a(this).attr("name");return!d&&b.settings.debug&&window.console&&console.error("%o has no name assigned",this),this.hasAttribute("contenteditable")&&(this.form=a(this).closest("form")[0],this.name=d),!(d in c||!b.objectLength(a(this).rules()))&&(c[d]=!0,!0)})},clean:function(b){return a(b)[0]},errors:function(){var b=this.settings.errorClass.split(" ").join(".");return a(this.settings.errorElement+"."+b,this.errorContext)},resetInternals:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=a([]),this.toHide=a([])},reset:function(){this.resetInternals(),this.currentElements=a([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(a){this.reset(),this.toHide=this.errorsFor(a)},elementValue:function(b){var c,d,e=a(b),f=b.type;return"radio"===f||"checkbox"===f?this.findByName(b.name).filter(":checked").val():"number"===f&&"undefined"!=typeof b.validity?b.validity.badInput?"NaN":e.val():(c=b.hasAttribute("contenteditable")?e.text():e.val(),"file"===f?"C:\\fakepath\\"===c.substr(0,12)?c.substr(12):(d=c.lastIndexOf("/"),d>=0?c.substr(d+1):(d=c.lastIndexOf("\\"),d>=0?c.substr(d+1):c)):"string"==typeof c?c.replace(/\r/g,""):c)},check:function(b){b=this.validationTargetFor(this.clean(b));var c,d,e,f,g=a(b).rules(),h=a.map(g,function(a,b){return b}).length,i=!1,j=this.elementValue(b);if("function"==typeof g.normalizer?f=g.normalizer:"function"==typeof this.settings.normalizer&&(f=this.settings.normalizer),f){if(j=f.call(b,j),"string"!=typeof j)throw new TypeError("The normalizer should return a string value.");delete g.normalizer}for(d in g){e={method:d,parameters:g[d]};try{if(c=a.validator.methods[d].call(this,j,b,e.parameters),"dependency-mismatch"===c&&1===h){i=!0;continue}if(i=!1,"pending"===c)return void(this.toHide=this.toHide.not(this.errorsFor(b)));if(!c)return this.formatAndAdd(b,e),!1}catch(k){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+b.id+", check the '"+e.method+"' method.",k),k instanceof TypeError&&(k.message+=". Exception occurred when checking element "+b.id+", check the '"+e.method+"' method."),k}}if(!i)return this.objectLength(g)&&this.successList.push(b),!0},customDataMessage:function(b,c){return a(b).data("msg"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase())||a(b).data("msg")},customMessage:function(a,b){var c=this.settings.messages[a];return c&&(c.constructor===String?c:c[b])},findDefined:function(){for(var a=0;aWarning: No message defined for "+b.name+""),e=/\$?\{(\d+)\}/g;return"function"==typeof d?d=d.call(this,c.parameters,b):e.test(d)&&(d=a.validator.format(d.replace(e,"{$1}"),c.parameters)),d},formatAndAdd:function(a,b){var c=this.defaultMessage(a,b);this.errorList.push({message:c,element:a,method:b.method}),this.errorMap[a.name]=c,this.submitted[a.name]=c},addWrapper:function(a){return this.settings.wrapper&&(a=a.add(a.parent(this.settings.wrapper))),a},defaultShowErrors:function(){var a,b,c;for(a=0;this.errorList[a];a++)c=this.errorList[a],this.settings.highlight&&this.settings.highlight.call(this,c.element,this.settings.errorClass,this.settings.validClass),this.showLabel(c.element,c.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(a=0;this.successList[a];a++)this.showLabel(this.successList[a]);if(this.settings.unhighlight)for(a=0,b=this.validElements();b[a];a++)this.settings.unhighlight.call(this,b[a],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return a(this.errorList).map(function(){return this.element})},showLabel:function(b,c){var d,e,f,g,h=this.errorsFor(b),i=this.idOrName(b),j=a(b).attr("aria-describedby");h.length?(h.removeClass(this.settings.validClass).addClass(this.settings.errorClass),h.html(c)):(h=a("<"+this.settings.errorElement+">").attr("id",i+"-error").addClass(this.settings.errorClass).html(c||""),d=h,this.settings.wrapper&&(d=h.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(d):this.settings.errorPlacement?this.settings.errorPlacement.call(this,d,a(b)):d.insertAfter(b),h.is("label")?h.attr("for",i):0===h.parents("label[for='"+this.escapeCssMeta(i)+"']").length&&(f=h.attr("id"),j?j.match(new RegExp("\\b"+this.escapeCssMeta(f)+"\\b"))||(j+=" "+f):j=f,a(b).attr("aria-describedby",j),e=this.groups[b.name],e&&(g=this,a.each(g.groups,function(b,c){c===e&&a("[name='"+g.escapeCssMeta(b)+"']",g.currentForm).attr("aria-describedby",h.attr("id"))})))),!c&&this.settings.success&&(h.text(""),"string"==typeof this.settings.success?h.addClass(this.settings.success):this.settings.success(h,b)),this.toShow=this.toShow.add(h)},errorsFor:function(b){var c=this.escapeCssMeta(this.idOrName(b)),d=a(b).attr("aria-describedby"),e="label[for='"+c+"'], label[for='"+c+"'] *";return d&&(e=e+", #"+this.escapeCssMeta(d).replace(/\s+/g,", #")),this.errors().filter(e)},escapeCssMeta:function(a){return a.replace(/([\\!"#$%&'()*+,.\/:;<=>?@\[\]^`{|}~])/g,"\\$1")},idOrName:function(a){return this.groups[a.name]||(this.checkable(a)?a.name:a.id||a.name)},validationTargetFor:function(b){return this.checkable(b)&&(b=this.findByName(b.name)),a(b).not(this.settings.ignore)[0]},checkable:function(a){return/radio|checkbox/i.test(a.type)},findByName:function(b){return a(this.currentForm).find("[name='"+this.escapeCssMeta(b)+"']")},getLength:function(b,c){switch(c.nodeName.toLowerCase()){case"select":return a("option:selected",c).length;case"input":if(this.checkable(c))return this.findByName(c.name).filter(":checked").length}return b.length},depend:function(a,b){return!this.dependTypes[typeof a]||this.dependTypes[typeof a](a,b)},dependTypes:{"boolean":function(a){return a},string:function(b,c){return!!a(b,c.form).length},"function":function(a,b){return a(b)}},optional:function(b){var c=this.elementValue(b);return!a.validator.methods.required.call(this,c,b)&&"dependency-mismatch"},startRequest:function(b){this.pending[b.name]||(this.pendingRequest++,a(b).addClass(this.settings.pendingClass),this.pending[b.name]=!0)},stopRequest:function(b,c){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[b.name],a(b).removeClass(this.settings.pendingClass),c&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(a(this.currentForm).submit(),this.submitButton&&a("input:hidden[name='"+this.submitButton.name+"']",this.currentForm).remove(),this.formSubmitted=!1):!c&&0===this.pendingRequest&&this.formSubmitted&&(a(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(b,c){return c="string"==typeof c&&c||"remote",a.data(b,"previousValue")||a.data(b,"previousValue",{old:null,valid:!0,message:this.defaultMessage(b,{method:c})})},destroy:function(){this.resetForm(),a(this.currentForm).off(".validate").removeData("validator").find(".validate-equalTo-blur").off(".validate-equalTo").removeClass("validate-equalTo-blur")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(b,c){b.constructor===String?this.classRuleSettings[b]=c:a.extend(this.classRuleSettings,b)},classRules:function(b){var c={},d=a(b).attr("class");return d&&a.each(d.split(" "),function(){this in a.validator.classRuleSettings&&a.extend(c,a.validator.classRuleSettings[this])}),c},normalizeAttributeRule:function(a,b,c,d){/min|max|step/.test(c)&&(null===b||/number|range|text/.test(b))&&(d=Number(d),isNaN(d)&&(d=void 0)),d||0===d?a[c]=d:b===c&&"range"!==b&&(a[c]=!0)},attributeRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)"required"===c?(d=b.getAttribute(c),""===d&&(d=!0),d=!!d):d=f.attr(c),this.normalizeAttributeRule(e,g,c,d);return e.maxlength&&/-1|2147483647|524288/.test(e.maxlength)&&delete e.maxlength,e},dataRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)d=f.data("rule"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase()),this.normalizeAttributeRule(e,g,c,d);return e},staticRules:function(b){var c={},d=a.data(b.form,"validator");return d.settings.rules&&(c=a.validator.normalizeRule(d.settings.rules[b.name])||{}),c},normalizeRules:function(b,c){return a.each(b,function(d,e){if(e===!1)return void delete b[d];if(e.param||e.depends){var f=!0;switch(typeof e.depends){case"string":f=!!a(e.depends,c.form).length;break;case"function":f=e.depends.call(c,c)}f?b[d]=void 0===e.param||e.param:(a.data(c.form,"validator").resetElements(a(c)),delete b[d])}}),a.each(b,function(d,e){b[d]=a.isFunction(e)&&"normalizer"!==d?e(c):e}),a.each(["minlength","maxlength"],function(){b[this]&&(b[this]=Number(b[this]))}),a.each(["rangelength","range"],function(){var c;b[this]&&(a.isArray(b[this])?b[this]=[Number(b[this][0]),Number(b[this][1])]:"string"==typeof b[this]&&(c=b[this].replace(/[\[\]]/g,"").split(/[\s,]+/),b[this]=[Number(c[0]),Number(c[1])]))}),a.validator.autoCreateRanges&&(null!=b.min&&null!=b.max&&(b.range=[b.min,b.max],delete b.min,delete b.max),null!=b.minlength&&null!=b.maxlength&&(b.rangelength=[b.minlength,b.maxlength],delete b.minlength,delete b.maxlength)),b},normalizeRule:function(b){if("string"==typeof b){var c={};a.each(b.split(/\s/),function(){c[this]=!0}),b=c}return b},addMethod:function(b,c,d){a.validator.methods[b]=c,a.validator.messages[b]=void 0!==d?d:a.validator.messages[b],c.length<3&&a.validator.addClassRules(b,a.validator.normalizeRule(b))},methods:{required:function(b,c,d){if(!this.depend(d,c))return"dependency-mismatch";if("select"===c.nodeName.toLowerCase()){var e=a(c).val();return e&&e.length>0}return this.checkable(c)?this.getLength(b,c)>0:b.length>0},email:function(a,b){return this.optional(b)||/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(a)},url:function(a,b){return this.optional(b)||/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[\/?#]\S*)?$/i.test(a)},date:function(a,b){return this.optional(b)||!/Invalid|NaN/.test(new Date(a).toString())},dateISO:function(a,b){return this.optional(b)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(a)},number:function(a,b){return this.optional(b)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(a)},digits:function(a,b){return this.optional(b)||/^\d+$/.test(a)},minlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d},maxlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e<=d},rangelength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d[0]&&e<=d[1]},min:function(a,b,c){return this.optional(b)||a>=c},max:function(a,b,c){return this.optional(b)||a<=c},range:function(a,b,c){return this.optional(b)||a>=c[0]&&a<=c[1]},step:function(b,c,d){var e,f=a(c).attr("type"),g="Step attribute on input type "+f+" is not supported.",h=["text","number","range"],i=new RegExp("\\b"+f+"\\b"),j=f&&!i.test(h.join()),k=function(a){var b=(""+a).match(/(?:\.(\d+))?$/);return b&&b[1]?b[1].length:0},l=function(a){return Math.round(a*Math.pow(10,e))},m=!0;if(j)throw new Error(g);return e=k(d),(k(b)>e||l(b)%l(d)!==0)&&(m=!1),this.optional(c)||m},equalTo:function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-equalTo-blur").length&&e.addClass("validate-equalTo-blur").on("blur.validate-equalTo",function(){a(c).valid()}),b===e.val()},remote:function(b,c,d,e){if(this.optional(c))return"dependency-mismatch";e="string"==typeof e&&e||"remote";var f,g,h,i=this.previousValue(c,e);return this.settings.messages[c.name]||(this.settings.messages[c.name]={}),i.originalMessage=i.originalMessage||this.settings.messages[c.name][e],this.settings.messages[c.name][e]=i.message,d="string"==typeof d&&{url:d}||d,h=a.param(a.extend({data:b},d.data)),i.old===h?i.valid:(i.old=h,f=this,this.startRequest(c),g={},g[c.name]=b,a.ajax(a.extend(!0,{mode:"abort",port:"validate"+c.name,dataType:"json",data:g,context:f.currentForm,success:function(a){var d,g,h,j=a===!0||"true"===a;f.settings.messages[c.name][e]=i.originalMessage,j?(h=f.formSubmitted,f.resetInternals(),f.toHide=f.errorsFor(c),f.formSubmitted=h,f.successList.push(c),f.invalid[c.name]=!1,f.showErrors()):(d={},g=a||f.defaultMessage(c,{method:e,parameters:b}),d[c.name]=i.message=g,f.invalid[c.name]=!0,f.showErrors(d)),i.valid=j,f.stopRequest(c,j)}},d)),"pending")}}});var b,c={};return a.ajaxPrefilter?a.ajaxPrefilter(function(a,b,d){var e=a.port;"abort"===a.mode&&(c[e]&&c[e].abort(),c[e]=d)}):(b=a.ajax,a.ajax=function(d){var e=("mode"in d?d:a.ajaxSettings).mode,f=("port"in d?d:a.ajaxSettings).port;return"abort"===e?(c[f]&&c[f].abort(),c[f]=b.apply(this,arguments),c[f]):b.apply(this,arguments)}),a}); -------------------------------------------------------------------------------- /BuildMonitor/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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TeamCity BuildMonitor 2 | =================== 3 | 4 | A simple build monitor for TeamCity using ASP.NET Core 2.2 with the following features: 5 | 6 | - Build configuration name 7 | - Active branch 8 | - Triggered-by user name 9 | - Running build completion percentage 10 | - Queued builds 11 | - Automatic refresh with a 20 seconds interval 12 | - Groups (shown as backend, frontend and tests in the screenshot below) 13 | - Displays all build configurations automatically (default) 14 | - Can be customized to display custom groups and build configurations 15 | 16 | ![](https://raw.githubusercontent.com/JohanGl/TeamCity_BuildMonitor/master/BuildMonitor.png) 17 | 18 | ---------- 19 | 20 | Installation 21 | ------------- 22 | 23 | Make sure that you have ASP.NET Core 2.2 installed. 24 | 25 | Open appsettings.json and enter your TeamCity server information under the TeamCity node. UserName and Password will be automatically read/overridden from a user secrets file if available. 26 | 27 | In the constructor of the class IndexModel, you can switch between using DefaultBuildMonitorModelHandler (shows all jobs in TeamCity automatically) or the CustomBuildMonitorModelHandler which allows you to customize what to display. You can customize your personal view by editing the file App_Data/Settings.xml. --------------------------------------------------------------------------------