├── .gitignore ├── LICENSE ├── WebApp.sln ├── WebApp ├── App_Start │ ├── BundleConfig.cs │ ├── FilterConfig.cs │ ├── RouteConfig.cs │ └── WebApiConfig.cs ├── Content │ ├── Site.css │ ├── bootstrap.css │ └── bootstrap.min.css ├── Controllers │ ├── HomeController.cs │ ├── SessionController.cs │ ├── SpeakerController.cs │ └── TenantController.cs ├── Global.asax ├── Global.asax.cs ├── Migrations │ ├── 201512140630036_InitialCreate.Designer.cs │ ├── 201512140630036_InitialCreate.cs │ ├── 201512140630036_InitialCreate.resx │ ├── 201512140634446_sessions-and-speakers.Designer.cs │ ├── 201512140634446_sessions-and-speakers.cs │ ├── 201512140634446_sessions-and-speakers.resx │ └── Configuration.cs ├── Models │ ├── MultiTenantContext.cs │ ├── Session.cs │ ├── Speaker.cs │ └── Tenant.cs ├── Project_Readme.html ├── Properties │ └── AssemblyInfo.cs ├── Scripts │ ├── _references.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── jquery-1.10.2.intellisense.js │ ├── jquery-1.10.2.js │ ├── jquery-1.10.2.min.js │ ├── jquery-1.10.2.min.map │ ├── jquery.validate-vsdoc.js │ ├── jquery.validate.js │ ├── jquery.validate.min.js │ ├── jquery.validate.unobtrusive.js │ ├── jquery.validate.unobtrusive.min.js │ ├── modernizr-2.6.2.js │ ├── respond.js │ └── respond.min.js ├── Views │ ├── Home │ │ ├── About.cshtml │ │ ├── Contact.cshtml │ │ └── Index.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ └── _Layout.cshtml │ ├── Tenant │ │ └── Index.cshtml │ ├── Web.config │ └── _ViewStart.cshtml ├── Web.Debug.config ├── Web.Release.config ├── Web.config ├── WebApp.csproj ├── favicon.ico ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── packages.config ├── readme.md └── readme.txt /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | 24 | # Visual Studio 2015 cache/options directory 25 | .vs/ 26 | # Uncomment if you have tasks that create the project's static files in wwwroot 27 | #wwwroot/ 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | # NUNIT 34 | *.VisualState.xml 35 | TestResult.xml 36 | 37 | # Build Results of an ATL Project 38 | [Dd]ebugPS/ 39 | [Rr]eleasePS/ 40 | dlldata.c 41 | 42 | # DNX 43 | project.lock.json 44 | artifacts/ 45 | 46 | *_i.c 47 | *_p.c 48 | *_i.h 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.tmp_proj 63 | *.log 64 | *.vspscc 65 | *.vssscc 66 | .builds 67 | *.pidb 68 | *.svclog 69 | *.scc 70 | 71 | # Chutzpah Test files 72 | _Chutzpah* 73 | 74 | # Visual C++ cache files 75 | ipch/ 76 | *.aps 77 | *.ncb 78 | *.opendb 79 | *.opensdf 80 | *.sdf 81 | *.cachefile 82 | 83 | # Visual Studio profiler 84 | *.psess 85 | *.vsp 86 | *.vspx 87 | *.sap 88 | 89 | # TFS 2012 Local Workspace 90 | $tf/ 91 | 92 | # Guidance Automation Toolkit 93 | *.gpState 94 | 95 | # ReSharper is a .NET coding add-in 96 | _ReSharper*/ 97 | *.[Rr]e[Ss]harper 98 | *.DotSettings.user 99 | 100 | # JustCode is a .NET coding add-in 101 | .JustCode 102 | 103 | # TeamCity is a build add-in 104 | _TeamCity* 105 | 106 | # DotCover is a Code Coverage Tool 107 | *.dotCover 108 | 109 | # NCrunch 110 | _NCrunch_* 111 | .*crunch*.local.xml 112 | nCrunchTemp_* 113 | 114 | # MightyMoose 115 | *.mm.* 116 | AutoTest.Net/ 117 | 118 | # Web workbench (sass) 119 | .sass-cache/ 120 | 121 | # Installshield output folder 122 | [Ee]xpress/ 123 | 124 | # DocProject is a documentation generator add-in 125 | DocProject/buildhelp/ 126 | DocProject/Help/*.HxT 127 | DocProject/Help/*.HxC 128 | DocProject/Help/*.hhc 129 | DocProject/Help/*.hhk 130 | DocProject/Help/*.hhp 131 | DocProject/Help/Html2 132 | DocProject/Help/html 133 | 134 | # Click-Once directory 135 | publish/ 136 | 137 | # Publish Web Output 138 | *.[Pp]ublish.xml 139 | *.azurePubxml 140 | # TODO: Comment the next line if you want to checkin your web deploy settings 141 | # but database connection strings (with potential passwords) will be unencrypted 142 | *.pubxml 143 | *.publishproj 144 | 145 | # NuGet Packages 146 | *.nupkg 147 | # The packages folder can be ignored because of Package Restore 148 | **/packages/* 149 | # except build/, which is used as an MSBuild target. 150 | !**/packages/build/ 151 | # Uncomment if necessary however generally it will be regenerated when needed 152 | #!**/packages/repositories.config 153 | # NuGet v3's project.json files produces more ignoreable files 154 | *.nuget.props 155 | *.nuget.targets 156 | 157 | # Microsoft Azure Build Output 158 | csx/ 159 | *.build.csdef 160 | 161 | # Microsoft Azure Emulator 162 | ecf/ 163 | rcf/ 164 | 165 | # Microsoft Azure ApplicationInsights config file 166 | ApplicationInsights.config 167 | 168 | # Windows Store app package directory 169 | AppPackages/ 170 | BundleArtifacts/ 171 | 172 | # Visual Studio cache files 173 | # files ending in .cache can be ignored 174 | *.[Cc]ache 175 | # but keep track of directories ending in .cache 176 | !*.[Cc]ache/ 177 | 178 | # Others 179 | ClientBin/ 180 | ~$* 181 | *~ 182 | *.dbmdl 183 | *.dbproj.schemaview 184 | *.pfx 185 | *.publishsettings 186 | node_modules/ 187 | orleans.codegen.cs 188 | 189 | # RIA/Silverlight projects 190 | Generated_Code/ 191 | 192 | # Backup & report files from converting an old project file 193 | # to a newer Visual Studio version. Backup files are not needed, 194 | # because we have git ;-) 195 | _UpgradeReport_Files/ 196 | Backup*/ 197 | UpgradeLog*.XML 198 | UpgradeLog*.htm 199 | 200 | # SQL Server files 201 | *.mdf 202 | *.ldf 203 | 204 | # Business Intelligence projects 205 | *.rdl.data 206 | *.bim.layout 207 | *.bim_*.settings 208 | 209 | # Microsoft Fakes 210 | FakesAssemblies/ 211 | 212 | # GhostDoc plugin setting file 213 | *.GhostDoc.xml 214 | 215 | # Node.js Tools for Visual Studio 216 | .ntvs_analysis.dat 217 | 218 | # Visual Studio 6 build log 219 | *.plg 220 | 221 | # Visual Studio 6 workspace options file 222 | *.opt 223 | 224 | # Visual Studio LightSwitch build output 225 | **/*.HTMLClient/GeneratedArtifacts 226 | **/*.DesktopClient/GeneratedArtifacts 227 | **/*.DesktopClient/ModelManifest.xml 228 | **/*.Server/GeneratedArtifacts 229 | **/*.Server/ModelManifest.xml 230 | _Pvt_Extensions 231 | 232 | # Paket dependency manager 233 | .paket/paket.exe 234 | 235 | # FAKE - F# Make 236 | .fake/ 237 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Peter Kellner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /WebApp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApp", "WebApp\WebApp.csproj", "{34C50F90-32C4-4180-AA7D-97876C447137}" 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 | {34C50F90-32C4-4180-AA7D-97876C447137}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {34C50F90-32C4-4180-AA7D-97876C447137}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {34C50F90-32C4-4180-AA7D-97876C447137}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {34C50F90-32C4-4180-AA7D-97876C447137}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /WebApp/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Optimization; 3 | 4 | namespace WebApp 5 | { 6 | public class BundleConfig 7 | { 8 | // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 9 | public static void RegisterBundles(BundleCollection bundles) 10 | { 11 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 12 | "~/Scripts/jquery-{version}.js")); 13 | 14 | bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 15 | "~/Scripts/jquery.validate*")); 16 | 17 | // Use the development version of Modernizr to develop with and learn from. Then, when you're 18 | // ready for production, use the build tool at http://modernizr.com to pick only the tests you need. 19 | bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 20 | "~/Scripts/modernizr-*")); 21 | 22 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 23 | "~/Scripts/bootstrap.js", 24 | "~/Scripts/respond.js")); 25 | 26 | bundles.Add(new StyleBundle("~/Content/css").Include( 27 | "~/Content/bootstrap.css", 28 | "~/Content/site.css")); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /WebApp/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Mvc; 3 | 4 | namespace WebApp 5 | { 6 | public class FilterConfig 7 | { 8 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 9 | { 10 | filters.Add(new HandleErrorAttribute()); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /WebApp/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Routing; 7 | 8 | namespace WebApp 9 | { 10 | public class RouteConfig 11 | { 12 | public static void RegisterRoutes(RouteCollection routes) 13 | { 14 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 15 | 16 | routes.MapRoute( 17 | name: "Default", 18 | url: "{controller}/{action}/{id}", 19 | defaults: new { controller = "Home", 20 | action = "Index", 21 | id = UrlParameter.Optional } 22 | ); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /WebApp/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web.Http; 5 | 6 | namespace WebApp 7 | { 8 | public static class WebApiConfig 9 | { 10 | public static void Register(HttpConfiguration config) 11 | { 12 | // Web API configuration and services 13 | 14 | // Web API routes 15 | config.MapHttpAttributeRoutes(); 16 | 17 | config.Routes.MapHttpRoute( 18 | name: "DefaultApi", 19 | routeTemplate: "api/{controller}/{id}", 20 | defaults: new { id = RouteParameter.Optional } 21 | ); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /WebApp/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Override the default bootstrap behavior where horizontal description lists 13 | will truncate terms that are too long to fit in the left column 14 | */ 15 | .dl-horizontal dt { 16 | white-space: normal; 17 | } 18 | 19 | /* Set width on the form input elements since they're 100% wide by default */ 20 | input, 21 | select, 22 | textarea { 23 | max-width: 280px; 24 | } 25 | -------------------------------------------------------------------------------- /WebApp/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | 7 | namespace WebApp.Controllers 8 | { 9 | public class HomeController : Controller 10 | { 11 | public ActionResult Index() 12 | { 13 | return View(); 14 | } 15 | 16 | public ActionResult About() 17 | { 18 | ViewBag.Message = "Your application description page."; 19 | 20 | return View(); 21 | } 22 | 23 | public ActionResult Contact() 24 | { 25 | ViewBag.Message = "Your contact page."; 26 | 27 | return View(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /WebApp/Controllers/SessionController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using WebApp.Models; 7 | 8 | namespace WebApp.Controllers 9 | { 10 | public class SessionController : Controller 11 | { 12 | public ActionResult Index() 13 | { 14 | return View(); 15 | } 16 | 17 | } 18 | } -------------------------------------------------------------------------------- /WebApp/Controllers/SpeakerController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using WebApp.Models; 7 | 8 | namespace WebApp.Controllers 9 | { 10 | public class SpeakerController : Controller 11 | { 12 | public ActionResult Index() 13 | { 14 | return View(); 15 | } 16 | 17 | } 18 | } -------------------------------------------------------------------------------- /WebApp/Controllers/TenantController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using System.Web; 7 | using System.Web.Mvc; 8 | using WebApp.Models; 9 | 10 | namespace WebApp.Controllers 11 | { 12 | public class TenantController : Controller 13 | { 14 | public async Task Index() 15 | { 16 | using (var context = new MultiTenantContext()) 17 | { 18 | var tenants = 19 | await context.Tenants.ToListAsync(); 20 | return View(tenants); 21 | } 22 | } 23 | 24 | } 25 | } -------------------------------------------------------------------------------- /WebApp/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="WebApp.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /WebApp/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Http; 6 | using System.Web.Mvc; 7 | using System.Web.Optimization; 8 | using System.Web.Routing; 9 | using WebApp.Models; 10 | 11 | namespace WebApp 12 | { 13 | public class MvcApplication : System.Web.HttpApplication 14 | { 15 | protected void Application_Start() 16 | { 17 | AreaRegistration.RegisterAllAreas(); 18 | GlobalConfiguration.Configure(WebApiConfig.Register); 19 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 20 | RouteConfig.RegisterRoutes(RouteTable.Routes); 21 | BundleConfig.RegisterBundles(BundleTable.Bundles); 22 | 23 | 24 | using (var context = new MultiTenantContext()) 25 | { 26 | if (!context.Speakers.Any()) 27 | { 28 | context.Speakers.Add(new Speaker() 29 | { 30 | LastName = Guid.NewGuid().ToString("N") 31 | }); 32 | context.Sessions.Add(new Session() 33 | { 34 | Title = Guid.NewGuid().ToString("N") 35 | }); 36 | context.SaveChanges(); 37 | } 38 | } 39 | 40 | 41 | using (var context = new MultiTenantContext()) 42 | { 43 | if (!context.Tenants.Any()) 44 | { 45 | var tenants = new List 46 | { 47 | new Tenant 48 | { 49 | Name = "SVCC", 50 | DomainName = "www.siliconvalley-codecamp.com", 51 | Id = 1, 52 | Default = true 53 | }, 54 | new Tenant() 55 | { 56 | Name = "ANGU", 57 | DomainName = "angularu.com", 58 | Id = 3, 59 | Default = false 60 | }, 61 | new Tenant() 62 | { 63 | Name = "CSSC", 64 | DomainName = "codestarssummit.com", 65 | Id = 2, 66 | Default = false 67 | } 68 | 69 | }; 70 | context.Tenants.AddRange(tenants); 71 | context.SaveChanges(); 72 | } 73 | } 74 | 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /WebApp/Migrations/201512140630036_InitialCreate.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace WebApp.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] 10 | public sealed partial class InitialCreate : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(InitialCreate)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201512140630036_InitialCreate"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /WebApp/Migrations/201512140630036_InitialCreate.cs: -------------------------------------------------------------------------------- 1 | namespace WebApp.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class InitialCreate : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | CreateTable( 11 | "dbo.Tenants", 12 | c => new 13 | { 14 | Id = c.Int(nullable: false, identity: true), 15 | Name = c.String(), 16 | DomainName = c.String(), 17 | Default = c.Boolean(nullable: false), 18 | }) 19 | .PrimaryKey(t => t.Id); 20 | 21 | } 22 | 23 | public override void Down() 24 | { 25 | DropTable("dbo.Tenants"); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /WebApp/Migrations/201512140630036_InitialCreate.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | H4sIAAAAAAAEAM1X227bOBB9X6D/IPBpF0jNXF52A6lFaieLYOskqNL2mZZGDlFetCQV2N/Wh35Sf2GH1l2yHSctioUBwxrNnDmcK/3967fw7UqK4BGM5VpF5GRyTAJQiU65WkakcNnrP8nbN69+Cy9TuQo+1XpnXg8tlY3Ig3P5OaU2eQDJ7ETyxGirMzdJtKQs1fT0+PgvenJCASEIYgVB+KFQjkvYPODjVKsEclcwMdcpCFvJ8U28QQ1umASbswQi8hkWF3k+KRVJcCE4QxIxiIwETCntmEOK5x8txM5otYxzFDBxv84B9TImLFTUz1v1Q09xfOpPQVvDGioprNPymYAnZ1VY6ND8RcElTdgwcJcYYLf2p94ELyL3oJhyJBi6Op8K49UGkZ2U+kdBKT1qco8l4j9HwbQQrjAQKSicYeIouCsWgif/wPpefwEVqUKILiUkhe96AhTdGZ2DcesPkFVEr1MS0L4dHRo2Zh2b8hDXyp2dkuAGnbOFgCbjnQPHThv4GxQY5iC9Y86BUR4DNjEbeR/48t+1NywxbBQSzNnqPaile4gI/iTBFV9BWksqBh8Vx75CI2cKeMrJDFPE1a9xBRnDVNZ+3mktgKktIezChLQtsHHZYT87pA+mcjFHfF4WlH8Fq211iB1blaKtiPdpl9AxuF5B4whomZRzYFKX+jbCDbV2xtByyNTDiO6YRuGc5TmmoDOdKkkQl6Np+jp+fuPKEoMmdkv/NmwbT1i7bAmDt+gamV5xY92MObZgPl3TVI7UtiRiR5Brh/1YD7u3DX2t73+XNtvGyRCgjeEVHktiA25OCA2NZm6NDDfLgQlmtoyCqRaFVLvGyT7rsuO69qXkcIRu53ZxuvJnoNXN2YOqhWOckA7iOcwYHaVsMGqHFbCvh4YqjfemlwY9E1b1+/SaHxV0qUICDNAjT30xx2vrQE68wiT+V0wFB9/1tcKcKZ6BdeU2IrhuTwfXhf/P6qbWpuKg/f3LFyr3MX1yZe5dFvt3qHpkJnlg5nfJVn90kV62J38Mrr8LF9z91D04ntKHrLg9G65sioikC42cS5bNXnzh+hu3aEi79/VwBpYvWwh/e1eQ+NpvQWuda5XpOsZ4ri6jWmWQgjk4lmJ4LozjGUscvk7A2s3d5xMTBapcygWk1+q2cHnhLqwFuRC9a1tI9/vf7Pg+5/A290/2ZxwBaXI8AtyqdwUXacP7akv97IDwlVL1G7LCux/CLdcN0o1WBwJV4ZtBDsp36z3IXCCYvVUxe4SXcMOL2ntYsmRdT9rdIE8noh/2cMbZ0jBpK4zW3v8Hpf5P6Jv/ANBiyRi2DgAA 122 | 123 | 124 | dbo 125 | 126 | -------------------------------------------------------------------------------- /WebApp/Migrations/201512140634446_sessions-and-speakers.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace WebApp.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] 10 | public sealed partial class sessionsandspeakers : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(sessionsandspeakers)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201512140634446_sessions-and-speakers"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /WebApp/Migrations/201512140634446_sessions-and-speakers.cs: -------------------------------------------------------------------------------- 1 | namespace WebApp.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class sessionsandspeakers : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | CreateTable( 11 | "dbo.Sessions", 12 | c => new 13 | { 14 | Id = c.Int(nullable: false, identity: true), 15 | SessionId = c.Int(nullable: false), 16 | Title = c.String(), 17 | DescriptionShort = c.String(), 18 | Description = c.String(), 19 | Tenant_Id = c.Int(), 20 | }) 21 | .PrimaryKey(t => t.Id) 22 | .ForeignKey("dbo.Tenants", t => t.Tenant_Id) 23 | .Index(t => t.Tenant_Id); 24 | 25 | CreateTable( 26 | "dbo.Speakers", 27 | c => new 28 | { 29 | Id = c.Int(nullable: false, identity: true), 30 | FirstName = c.String(), 31 | LastName = c.String(), 32 | ImageUrl = c.String(), 33 | WebSite = c.String(), 34 | Bio = c.String(), 35 | AllowHtml = c.Boolean(nullable: false), 36 | PictureId = c.Int(nullable: false), 37 | }) 38 | .PrimaryKey(t => t.Id); 39 | 40 | CreateTable( 41 | "dbo.SpeakerSessions", 42 | c => new 43 | { 44 | Speaker_Id = c.Int(nullable: false), 45 | Session_Id = c.Int(nullable: false), 46 | }) 47 | .PrimaryKey(t => new { t.Speaker_Id, t.Session_Id }) 48 | .ForeignKey("dbo.Speakers", t => t.Speaker_Id, cascadeDelete: true) 49 | .ForeignKey("dbo.Sessions", t => t.Session_Id, cascadeDelete: true) 50 | .Index(t => t.Speaker_Id) 51 | .Index(t => t.Session_Id); 52 | 53 | } 54 | 55 | public override void Down() 56 | { 57 | DropForeignKey("dbo.Sessions", "Tenant_Id", "dbo.Tenants"); 58 | DropForeignKey("dbo.SpeakerSessions", "Session_Id", "dbo.Sessions"); 59 | DropForeignKey("dbo.SpeakerSessions", "Speaker_Id", "dbo.Speakers"); 60 | DropIndex("dbo.SpeakerSessions", new[] { "Session_Id" }); 61 | DropIndex("dbo.SpeakerSessions", new[] { "Speaker_Id" }); 62 | DropIndex("dbo.Sessions", new[] { "Tenant_Id" }); 63 | DropTable("dbo.SpeakerSessions"); 64 | DropTable("dbo.Speakers"); 65 | DropTable("dbo.Sessions"); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /WebApp/Migrations/201512140634446_sessions-and-speakers.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | H4sIAAAAAAAEAO1bzW7jNhC+F+g7CDq1RdZKspc2sHeRdTat0c0P4mTbW0BLtEOUolSRSh0UfbIe+kh9hZL6oSlSlCjbSReLxV4civw4M5wZzg/337//Gb9dx9h7hBlFCZn4R6ND34MkTCJEVhM/Z8tX3/tv33z91fh9FK+9j/W812IeX0noxH9gLD0JAho+wBjQUYzCLKHJko3CJA5AlATHh4c/BEdHAeQQPsfyvPFNThiKYfEH/3OakBCmLAf4IokgptU4/zIvUL1LEEOaghBO/F/g4jRNR+VE3zvFCHAi5hAvfQ8QkjDAOIkndxTOWZaQ1TzlAwDfPqWQz1sCTGFF+slmuisXh8eCi2CzsIYKc8qSeCDg0etKLIG+fCvh+lJsXHDvuYDZk+C6EJ6QEKUFsr7XyRRnYp4m2lG14MArhw/k6XMlEf8OvGmOWZ7BCYE5ywA+8K7zBUbhz/DpNvkNkgnJMVaJ4mTxb40BPnSdJSnM2NMNXFakziLfC5rrAn2hXKasKbmYEfb62Pcu+eZggaE8c4XjOUsy+CMkMAMMRteAMZgRgQELqRm7a3tVkunfshvmFjEMawiurNzkfO8CrD9AsmIPE5//9L1ztIZRPVLB3hHELZQvYlneu8sZpGGGUsH3/CHJ2EtuuP+9LsEjWhWHqJ9JCsFvXEF97wbiYgJ9QGnpGEbVx/vq4Pik8yyJbxK8WSi/3d+CbAWFmBLLhHmSZ+EA0m4hAYS1E1Zi3tdTFLIaX+Sekqjm55pmlaRxsPEB3Z6hZHCAZygXfPEMxl7nKKNM/Hx2K/sAXmijWQxW8C7Dz74RV6Y5Ys/P0DuUPPsepxgnf/zEYim1dwk3W0AG3xHXKBSGNPiqsftJ6QJ39ZOGS7I50q2cUu0RXX1SOf+LSzL2ehEnccaPCJGX2QouAT/KYYZlU7tTSpMQFXJuXoYbE2gS855EXp89lPwr9sSlwClGKVc3TsPE/87gsQNWhiMKbB3Kd8KOA4W7HqabYYiVNktMMpSyLtAWdmtX0MQ8HI2OHBkuz54nmYwrKcwqpgu4Elt8gus2b8PTyMrh0Eo9dT4E9Bwyw71uFE6XjSEKDUJGsiZErU89ECVXbQi1LDUARXAGIcptoEyz3Ri6w3QwGEm9ynswAKjWGRVI0qz75iarLmLQYvQWIXSYj4sBOdDtYjIKjFQAZ+5rDymtZFODCcoiTF2sCSzVmvEFSFPu85XqTTXizcvSzfTVfHhhIy4xgpC21DcktXInflnyeFX7yrfmlBbB+RlgYAHE/TCNYmNai0+wWFq9oWb25qHVBlgvEL9lwGuWW3SEjRTPOWMxv/MLHqGpM+bKon4GMMhawo9pgvOY2EKYrtVK9UMFUYbdsaoSiIpTDbljmAUOFc78uhWyFbQNbxxoR6ZrRWCohWamupq5KWHtPHdQwuqC2UIJbSufRwmVRFsFUYbdsTaptAq1GXVH2uTKDc7kqDuSTIZVIDnojlMkuypGMeC+XklkVRRl2B1LyWVVLGX4kzGl+v7c3pLaYwEHQ7ItfB47MvV+qM6rqV/DRyrjQ1xuld013W01+PL60YyXWv2tEh/3uFVlpsV9Wq5yEfu117uNaNqUkJPW1HBt2iNkJLffgrIqPt2WsirYHUaZHukOP1wt6u8J2+Q8x+CsTW6tUf2WUqtA9nGcrTnLlmQNpodH4REqKhUzKio8srrjxGy/Fhhpjz5FOhiZ/mhpzrhKOfo710YOUk7xPc78I4pE/jF/ogzGIzFhNP8dTzGCQqnqCReAoCWkrKxY+seHR8daB/zT6UYHlEbYrSX94lVXJC21q646sFRvdIWLXXbpCZNHkIUPIPsmButvVahd+r77At0LnmK5isyMKtyMRHA98f8sVp14s1/v5cID7yrjlnHiHXp/de8+uB/6eail0ZLc6cD0tuNOYHprcScwrX24E5bSItwJx2gDLtBwl2C0APv9ytAu2+eh6XvTS7ObtaPTbHSs+nVgqKfa4R7tjPtbpluD8f7TV7ca5uw3K23efsgFvQ0BcuUAArbvObYG2WYDxKXVeGQkHlfkDGLIoHcalqHhFNAQRKb0RFjeQ4FsaZmEOHcCuXLBTFg8wDwIpyzjpmdk8dcZIiFKAdYkYKYhLp5KMCcR9S9nMIVEuKB2Vl127E6m5QaauPsEMaSf65KCmw0ll1bu56pQA473f1CozhrISyiU2wMBtVgysHm/rT7u6fyt5dr9H/+Ac++s4uz92NufSZidU0sFXX9nbn8AUdY9Jn60EGF2eYFaG/aW5xFdryNa8S0vCyxvJzqeTrSh2/rtFuI7ZKRN6GDFKjGnxxzy8UH/mw5Lza8tIGlpCDq85NDE0VYQ38MzDturEQcBtNdi2xxWS+X3kxHA/t6xNG1EbVs5PFtxEdJ+HqqYlVnuHZX/ecR9M0WrDYSoNhMYNvyinDMjy6T20BpF9RQt37iADETcaZ5mDC1ByPjnULArnkV+BDjnU97HCxjNyFXO0pxxlmG8wI0XncLNd+1fvMZp0jy+KupzdB8scDIRZwFekXc5wpGk+7wl3bFAiPujSvLFWTKR7K+eJNJlQhyBKvHJa+8WxinmYPSKzMEj3Ia2Owo/wBUIn+oCux2k/yCaYh+fIbDKQEwrjM16/ifX4Shev/kPzMARzYA3AAA= 122 | 123 | 124 | dbo 125 | 126 | -------------------------------------------------------------------------------- /WebApp/Migrations/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace WebApp.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity; 5 | using System.Data.Entity.Migrations; 6 | using System.Linq; 7 | 8 | internal sealed class Configuration : DbMigrationsConfiguration 9 | { 10 | public Configuration() 11 | { 12 | AutomaticMigrationsEnabled = false; 13 | ContextKey = "WebApp.Models.MultiTenantContext"; 14 | } 15 | 16 | protected override void Seed(WebApp.Models.MultiTenantContext context) 17 | { 18 | // This method will be called after migrating to the latest version. 19 | 20 | // You can use the DbSet.AddOrUpdate() helper extension method 21 | // to avoid creating duplicate seed data. E.g. 22 | // 23 | // context.People.AddOrUpdate( 24 | // p => p.FullName, 25 | // new Person { FullName = "Andrew Peters" }, 26 | // new Person { FullName = "Brice Lambson" }, 27 | // new Person { FullName = "Rowan Miller" } 28 | // ); 29 | // 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /WebApp/Models/MultiTenantContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity; 4 | using System.Linq; 5 | using System.Web; 6 | 7 | namespace WebApp.Models 8 | { 9 | public class MultiTenantContext : DbContext 10 | { 11 | public DbSet Tenants { get; set; } 12 | public DbSet Speakers { get; set; } 13 | public DbSet Sessions { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /WebApp/Models/Session.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace WebApp.Models 7 | { 8 | public class Session 9 | { 10 | public int Id { get; set; } 11 | public int SessionId { get; set; } 12 | public string Title { get; set; } 13 | public string DescriptionShort { get; set; } 14 | public string Description { get; set; } 15 | 16 | public Tenant Tenant { get; set; } 17 | public List Speakers { get; set; } 18 | 19 | } 20 | } -------------------------------------------------------------------------------- /WebApp/Models/Speaker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace WebApp.Models 7 | { 8 | public class Speaker 9 | { 10 | public int Id { get; set; } 11 | public string FirstName { get; set; } 12 | public string LastName { get; set; } 13 | public string ImageUrl { get; set; } 14 | public string WebSite { get; set; } 15 | public string Bio { get; set; } 16 | public bool AllowHtml { get; set; } 17 | public int PictureId { get; set; } 18 | 19 | public List Sessions { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /WebApp/Models/Tenant.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace WebApp.Models 7 | { 8 | public class Tenant 9 | { 10 | public int Id { get; set; } 11 | public string Name { get; set; } 12 | public string DomainName { get; set; } 13 | public bool Default { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /WebApp/Project_Readme.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Your ASP.NET application 6 | 95 | 96 | 97 | 98 | 102 | 103 |
104 |
105 |

This application consists of:

106 |
    107 |
  • Sample pages showing basic nav between Home, About, and Contact
  • 108 |
  • Theming using Bootstrap
  • 109 |
  • Authentication, if selected, shows how to register and sign in
  • 110 |
  • ASP.NET features managed using NuGet
  • 111 |
112 |
113 | 114 | 131 | 132 |
133 |

Deploy

134 | 139 |
140 | 141 |
142 |

Get help

143 | 147 |
148 |
149 | 150 | 151 | -------------------------------------------------------------------------------- /WebApp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("WebApp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("WebApp")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("89b7689e-c00f-4601-8e4b-81436ea6133e")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /WebApp/Scripts/_references.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellner/pluralsight-multitenantaspnet/8bf05004d84d19ff95a75f0bfd130f76c76fbac0/WebApp/Scripts/_references.js -------------------------------------------------------------------------------- /WebApp/Scripts/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | 16 | /** 17 | * bootstrap.js v3.0.0 by @fat and @mdo 18 | * Copyright 2013 Twitter Inc. 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | */ 21 | if(!jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(window.jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]');if(a.length){var b=this.$element.find("input").prop("checked",!this.$element.hasClass("active")).trigger("change");"radio"===b.prop("type")&&a.find(".active").removeClass("active")}this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(window.jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(window.jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery); -------------------------------------------------------------------------------- /WebApp/Scripts/jquery.validate.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! 16 | * jQuery Validation Plugin 1.11.1 17 | * 18 | * http://bassistance.de/jquery-plugins/jquery-plugin-validation/ 19 | * http://docs.jquery.com/Plugins/Validation 20 | * 21 | * Copyright 2013 Jörn Zaefferer 22 | * Released under the MIT license: 23 | * http://www.opensource.org/licenses/mit-license.php 24 | */ 25 | 26 | (function($) { 27 | 28 | $.extend($.fn, { 29 | // http://docs.jquery.com/Plugins/Validation/validate 30 | validate: function( options ) { 31 | 32 | // if nothing is selected, return nothing; can't chain anyway 33 | if ( !this.length ) { 34 | if ( options && options.debug && window.console ) { 35 | console.warn( "Nothing selected, can't validate, returning nothing." ); 36 | } 37 | return; 38 | } 39 | 40 | // check if a validator for this form was already created 41 | var validator = $.data( this[0], "validator" ); 42 | if ( validator ) { 43 | return validator; 44 | } 45 | 46 | // Add novalidate tag if HTML5. 47 | this.attr( "novalidate", "novalidate" ); 48 | 49 | validator = new $.validator( options, this[0] ); 50 | $.data( this[0], "validator", validator ); 51 | 52 | if ( validator.settings.onsubmit ) { 53 | 54 | this.validateDelegate( ":submit", "click", function( event ) { 55 | if ( validator.settings.submitHandler ) { 56 | validator.submitButton = event.target; 57 | } 58 | // allow suppressing validation by adding a cancel class to the submit button 59 | if ( $(event.target).hasClass("cancel") ) { 60 | validator.cancelSubmit = true; 61 | } 62 | 63 | // allow suppressing validation by adding the html5 formnovalidate attribute to the submit button 64 | if ( $(event.target).attr("formnovalidate") !== undefined ) { 65 | validator.cancelSubmit = true; 66 | } 67 | }); 68 | 69 | // validate the form on submit 70 | this.submit( function( event ) { 71 | if ( validator.settings.debug ) { 72 | // prevent form submit to be able to see console output 73 | event.preventDefault(); 74 | } 75 | function handle() { 76 | var hidden; 77 | if ( validator.settings.submitHandler ) { 78 | if ( validator.submitButton ) { 79 | // insert a hidden input as a replacement for the missing submit button 80 | hidden = $("").attr("name", validator.submitButton.name).val( $(validator.submitButton).val() ).appendTo(validator.currentForm); 81 | } 82 | validator.settings.submitHandler.call( validator, validator.currentForm, event ); 83 | if ( validator.submitButton ) { 84 | // and clean up afterwards; thanks to no-block-scope, hidden can be referenced 85 | hidden.remove(); 86 | } 87 | return false; 88 | } 89 | return true; 90 | } 91 | 92 | // prevent submit for invalid forms or custom submit handlers 93 | if ( validator.cancelSubmit ) { 94 | validator.cancelSubmit = false; 95 | return handle(); 96 | } 97 | if ( validator.form() ) { 98 | if ( validator.pendingRequest ) { 99 | validator.formSubmitted = true; 100 | return false; 101 | } 102 | return handle(); 103 | } else { 104 | validator.focusInvalid(); 105 | return false; 106 | } 107 | }); 108 | } 109 | 110 | return validator; 111 | }, 112 | // http://docs.jquery.com/Plugins/Validation/valid 113 | valid: function() { 114 | if ( $(this[0]).is("form")) { 115 | return this.validate().form(); 116 | } else { 117 | var valid = true; 118 | var validator = $(this[0].form).validate(); 119 | this.each(function() { 120 | valid = valid && validator.element(this); 121 | }); 122 | return valid; 123 | } 124 | }, 125 | // attributes: space seperated list of attributes to retrieve and remove 126 | removeAttrs: function( attributes ) { 127 | var result = {}, 128 | $element = this; 129 | $.each(attributes.split(/\s/), function( index, value ) { 130 | result[value] = $element.attr(value); 131 | $element.removeAttr(value); 132 | }); 133 | return result; 134 | }, 135 | // http://docs.jquery.com/Plugins/Validation/rules 136 | rules: function( command, argument ) { 137 | var element = this[0]; 138 | 139 | if ( command ) { 140 | var settings = $.data(element.form, "validator").settings; 141 | var staticRules = settings.rules; 142 | var existingRules = $.validator.staticRules(element); 143 | switch(command) { 144 | case "add": 145 | $.extend(existingRules, $.validator.normalizeRule(argument)); 146 | // remove messages from rules, but allow them to be set separetely 147 | delete existingRules.messages; 148 | staticRules[element.name] = existingRules; 149 | if ( argument.messages ) { 150 | settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages ); 151 | } 152 | break; 153 | case "remove": 154 | if ( !argument ) { 155 | delete staticRules[element.name]; 156 | return existingRules; 157 | } 158 | var filtered = {}; 159 | $.each(argument.split(/\s/), function( index, method ) { 160 | filtered[method] = existingRules[method]; 161 | delete existingRules[method]; 162 | }); 163 | return filtered; 164 | } 165 | } 166 | 167 | var data = $.validator.normalizeRules( 168 | $.extend( 169 | {}, 170 | $.validator.classRules(element), 171 | $.validator.attributeRules(element), 172 | $.validator.dataRules(element), 173 | $.validator.staticRules(element) 174 | ), element); 175 | 176 | // make sure required is at front 177 | if ( data.required ) { 178 | var param = data.required; 179 | delete data.required; 180 | data = $.extend({required: param}, data); 181 | } 182 | 183 | return data; 184 | } 185 | }); 186 | 187 | // Custom selectors 188 | $.extend($.expr[":"], { 189 | // http://docs.jquery.com/Plugins/Validation/blank 190 | blank: function( a ) { return !$.trim("" + $(a).val()); }, 191 | // http://docs.jquery.com/Plugins/Validation/filled 192 | filled: function( a ) { return !!$.trim("" + $(a).val()); }, 193 | // http://docs.jquery.com/Plugins/Validation/unchecked 194 | unchecked: function( a ) { return !$(a).prop("checked"); } 195 | }); 196 | 197 | // constructor for validator 198 | $.validator = function( options, form ) { 199 | this.settings = $.extend( true, {}, $.validator.defaults, options ); 200 | this.currentForm = form; 201 | this.init(); 202 | }; 203 | 204 | $.validator.format = function( source, params ) { 205 | if ( arguments.length === 1 ) { 206 | return function() { 207 | var args = $.makeArray(arguments); 208 | args.unshift(source); 209 | return $.validator.format.apply( this, args ); 210 | }; 211 | } 212 | if ( arguments.length > 2 && params.constructor !== Array ) { 213 | params = $.makeArray(arguments).slice(1); 214 | } 215 | if ( params.constructor !== Array ) { 216 | params = [ params ]; 217 | } 218 | $.each(params, function( i, n ) { 219 | source = source.replace( new RegExp("\\{" + i + "\\}", "g"), function() { 220 | return n; 221 | }); 222 | }); 223 | return source; 224 | }; 225 | 226 | $.extend($.validator, { 227 | 228 | defaults: { 229 | messages: {}, 230 | groups: {}, 231 | rules: {}, 232 | errorClass: "error", 233 | validClass: "valid", 234 | errorElement: "label", 235 | focusInvalid: true, 236 | errorContainer: $([]), 237 | errorLabelContainer: $([]), 238 | onsubmit: true, 239 | ignore: ":hidden", 240 | ignoreTitle: false, 241 | onfocusin: function( element, event ) { 242 | this.lastActive = element; 243 | 244 | // hide error label and remove error class on focus if enabled 245 | if ( this.settings.focusCleanup && !this.blockFocusCleanup ) { 246 | if ( this.settings.unhighlight ) { 247 | this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass ); 248 | } 249 | this.addWrapper(this.errorsFor(element)).hide(); 250 | } 251 | }, 252 | onfocusout: function( element, event ) { 253 | if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) { 254 | this.element(element); 255 | } 256 | }, 257 | onkeyup: function( element, event ) { 258 | if ( event.which === 9 && this.elementValue(element) === "" ) { 259 | return; 260 | } else if ( element.name in this.submitted || element === this.lastElement ) { 261 | this.element(element); 262 | } 263 | }, 264 | onclick: function( element, event ) { 265 | // click on selects, radiobuttons and checkboxes 266 | if ( element.name in this.submitted ) { 267 | this.element(element); 268 | } 269 | // or option elements, check parent select in that case 270 | else if ( element.parentNode.name in this.submitted ) { 271 | this.element(element.parentNode); 272 | } 273 | }, 274 | highlight: function( element, errorClass, validClass ) { 275 | if ( element.type === "radio" ) { 276 | this.findByName(element.name).addClass(errorClass).removeClass(validClass); 277 | } else { 278 | $(element).addClass(errorClass).removeClass(validClass); 279 | } 280 | }, 281 | unhighlight: function( element, errorClass, validClass ) { 282 | if ( element.type === "radio" ) { 283 | this.findByName(element.name).removeClass(errorClass).addClass(validClass); 284 | } else { 285 | $(element).removeClass(errorClass).addClass(validClass); 286 | } 287 | } 288 | }, 289 | 290 | // http://docs.jquery.com/Plugins/Validation/Validator/setDefaults 291 | setDefaults: function( settings ) { 292 | $.extend( $.validator.defaults, settings ); 293 | }, 294 | 295 | messages: { 296 | required: "This field is required.", 297 | remote: "Please fix this field.", 298 | email: "Please enter a valid email address.", 299 | url: "Please enter a valid URL.", 300 | date: "Please enter a valid date.", 301 | dateISO: "Please enter a valid date (ISO).", 302 | number: "Please enter a valid number.", 303 | digits: "Please enter only digits.", 304 | creditcard: "Please enter a valid credit card number.", 305 | equalTo: "Please enter the same value again.", 306 | maxlength: $.validator.format("Please enter no more than {0} characters."), 307 | minlength: $.validator.format("Please enter at least {0} characters."), 308 | rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."), 309 | range: $.validator.format("Please enter a value between {0} and {1}."), 310 | max: $.validator.format("Please enter a value less than or equal to {0}."), 311 | min: $.validator.format("Please enter a value greater than or equal to {0}.") 312 | }, 313 | 314 | autoCreateRanges: false, 315 | 316 | prototype: { 317 | 318 | init: function() { 319 | this.labelContainer = $(this.settings.errorLabelContainer); 320 | this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm); 321 | this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer ); 322 | this.submitted = {}; 323 | this.valueCache = {}; 324 | this.pendingRequest = 0; 325 | this.pending = {}; 326 | this.invalid = {}; 327 | this.reset(); 328 | 329 | var groups = (this.groups = {}); 330 | $.each(this.settings.groups, function( key, value ) { 331 | if ( typeof value === "string" ) { 332 | value = value.split(/\s/); 333 | } 334 | $.each(value, function( index, name ) { 335 | groups[name] = key; 336 | }); 337 | }); 338 | var rules = this.settings.rules; 339 | $.each(rules, function( key, value ) { 340 | rules[key] = $.validator.normalizeRule(value); 341 | }); 342 | 343 | function delegate(event) { 344 | var validator = $.data(this[0].form, "validator"), 345 | eventType = "on" + event.type.replace(/^validate/, ""); 346 | if ( validator.settings[eventType] ) { 347 | validator.settings[eventType].call(validator, this[0], event); 348 | } 349 | } 350 | $(this.currentForm) 351 | .validateDelegate(":text, [type='password'], [type='file'], select, textarea, " + 352 | "[type='number'], [type='search'] ,[type='tel'], [type='url'], " + 353 | "[type='email'], [type='datetime'], [type='date'], [type='month'], " + 354 | "[type='week'], [type='time'], [type='datetime-local'], " + 355 | "[type='range'], [type='color'] ", 356 | "focusin focusout keyup", delegate) 357 | .validateDelegate("[type='radio'], [type='checkbox'], select, option", "click", delegate); 358 | 359 | if ( this.settings.invalidHandler ) { 360 | $(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler); 361 | } 362 | }, 363 | 364 | // http://docs.jquery.com/Plugins/Validation/Validator/form 365 | form: function() { 366 | this.checkForm(); 367 | $.extend(this.submitted, this.errorMap); 368 | this.invalid = $.extend({}, this.errorMap); 369 | if ( !this.valid() ) { 370 | $(this.currentForm).triggerHandler("invalid-form", [this]); 371 | } 372 | this.showErrors(); 373 | return this.valid(); 374 | }, 375 | 376 | checkForm: function() { 377 | this.prepareForm(); 378 | for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) { 379 | this.check( elements[i] ); 380 | } 381 | return this.valid(); 382 | }, 383 | 384 | // http://docs.jquery.com/Plugins/Validation/Validator/element 385 | element: function( element ) { 386 | element = this.validationTargetFor( this.clean( element ) ); 387 | this.lastElement = element; 388 | this.prepareElement( element ); 389 | this.currentElements = $(element); 390 | var result = this.check( element ) !== false; 391 | if ( result ) { 392 | delete this.invalid[element.name]; 393 | } else { 394 | this.invalid[element.name] = true; 395 | } 396 | if ( !this.numberOfInvalids() ) { 397 | // Hide error containers on last error 398 | this.toHide = this.toHide.add( this.containers ); 399 | } 400 | this.showErrors(); 401 | return result; 402 | }, 403 | 404 | // http://docs.jquery.com/Plugins/Validation/Validator/showErrors 405 | showErrors: function( errors ) { 406 | if ( errors ) { 407 | // add items to error list and map 408 | $.extend( this.errorMap, errors ); 409 | this.errorList = []; 410 | for ( var name in errors ) { 411 | this.errorList.push({ 412 | message: errors[name], 413 | element: this.findByName(name)[0] 414 | }); 415 | } 416 | // remove items from success list 417 | this.successList = $.grep( this.successList, function( element ) { 418 | return !(element.name in errors); 419 | }); 420 | } 421 | if ( this.settings.showErrors ) { 422 | this.settings.showErrors.call( this, this.errorMap, this.errorList ); 423 | } else { 424 | this.defaultShowErrors(); 425 | } 426 | }, 427 | 428 | // http://docs.jquery.com/Plugins/Validation/Validator/resetForm 429 | resetForm: function() { 430 | if ( $.fn.resetForm ) { 431 | $(this.currentForm).resetForm(); 432 | } 433 | this.submitted = {}; 434 | this.lastElement = null; 435 | this.prepareForm(); 436 | this.hideErrors(); 437 | this.elements().removeClass( this.settings.errorClass ).removeData( "previousValue" ); 438 | }, 439 | 440 | numberOfInvalids: function() { 441 | return this.objectLength(this.invalid); 442 | }, 443 | 444 | objectLength: function( obj ) { 445 | var count = 0; 446 | for ( var i in obj ) { 447 | count++; 448 | } 449 | return count; 450 | }, 451 | 452 | hideErrors: function() { 453 | this.addWrapper( this.toHide ).hide(); 454 | }, 455 | 456 | valid: function() { 457 | return this.size() === 0; 458 | }, 459 | 460 | size: function() { 461 | return this.errorList.length; 462 | }, 463 | 464 | focusInvalid: function() { 465 | if ( this.settings.focusInvalid ) { 466 | try { 467 | $(this.findLastActive() || this.errorList.length && this.errorList[0].element || []) 468 | .filter(":visible") 469 | .focus() 470 | // manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find 471 | .trigger("focusin"); 472 | } catch(e) { 473 | // ignore IE throwing errors when focusing hidden elements 474 | } 475 | } 476 | }, 477 | 478 | findLastActive: function() { 479 | var lastActive = this.lastActive; 480 | return lastActive && $.grep(this.errorList, function( n ) { 481 | return n.element.name === lastActive.name; 482 | }).length === 1 && lastActive; 483 | }, 484 | 485 | elements: function() { 486 | var validator = this, 487 | rulesCache = {}; 488 | 489 | // select all valid inputs inside the form (no submit or reset buttons) 490 | return $(this.currentForm) 491 | .find("input, select, textarea") 492 | .not(":submit, :reset, :image, [disabled]") 493 | .not( this.settings.ignore ) 494 | .filter(function() { 495 | if ( !this.name && validator.settings.debug && window.console ) { 496 | console.error( "%o has no name assigned", this); 497 | } 498 | 499 | // select only the first element for each name, and only those with rules specified 500 | if ( this.name in rulesCache || !validator.objectLength($(this).rules()) ) { 501 | return false; 502 | } 503 | 504 | rulesCache[this.name] = true; 505 | return true; 506 | }); 507 | }, 508 | 509 | clean: function( selector ) { 510 | return $(selector)[0]; 511 | }, 512 | 513 | errors: function() { 514 | var errorClass = this.settings.errorClass.replace(" ", "."); 515 | return $(this.settings.errorElement + "." + errorClass, this.errorContext); 516 | }, 517 | 518 | reset: function() { 519 | this.successList = []; 520 | this.errorList = []; 521 | this.errorMap = {}; 522 | this.toShow = $([]); 523 | this.toHide = $([]); 524 | this.currentElements = $([]); 525 | }, 526 | 527 | prepareForm: function() { 528 | this.reset(); 529 | this.toHide = this.errors().add( this.containers ); 530 | }, 531 | 532 | prepareElement: function( element ) { 533 | this.reset(); 534 | this.toHide = this.errorsFor(element); 535 | }, 536 | 537 | elementValue: function( element ) { 538 | var type = $(element).attr("type"), 539 | val = $(element).val(); 540 | 541 | if ( type === "radio" || type === "checkbox" ) { 542 | return $("input[name='" + $(element).attr("name") + "']:checked").val(); 543 | } 544 | 545 | if ( typeof val === "string" ) { 546 | return val.replace(/\r/g, ""); 547 | } 548 | return val; 549 | }, 550 | 551 | check: function( element ) { 552 | element = this.validationTargetFor( this.clean( element ) ); 553 | 554 | var rules = $(element).rules(); 555 | var dependencyMismatch = false; 556 | var val = this.elementValue(element); 557 | var result; 558 | 559 | for (var method in rules ) { 560 | var rule = { method: method, parameters: rules[method] }; 561 | try { 562 | 563 | result = $.validator.methods[method].call( this, val, element, rule.parameters ); 564 | 565 | // if a method indicates that the field is optional and therefore valid, 566 | // don't mark it as valid when there are no other rules 567 | if ( result === "dependency-mismatch" ) { 568 | dependencyMismatch = true; 569 | continue; 570 | } 571 | dependencyMismatch = false; 572 | 573 | if ( result === "pending" ) { 574 | this.toHide = this.toHide.not( this.errorsFor(element) ); 575 | return; 576 | } 577 | 578 | if ( !result ) { 579 | this.formatAndAdd( element, rule ); 580 | return false; 581 | } 582 | } catch(e) { 583 | if ( this.settings.debug && window.console ) { 584 | console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e ); 585 | } 586 | throw e; 587 | } 588 | } 589 | if ( dependencyMismatch ) { 590 | return; 591 | } 592 | if ( this.objectLength(rules) ) { 593 | this.successList.push(element); 594 | } 595 | return true; 596 | }, 597 | 598 | // return the custom message for the given element and validation method 599 | // specified in the element's HTML5 data attribute 600 | customDataMessage: function( element, method ) { 601 | return $(element).data("msg-" + method.toLowerCase()) || (element.attributes && $(element).attr("data-msg-" + method.toLowerCase())); 602 | }, 603 | 604 | // return the custom message for the given element name and validation method 605 | customMessage: function( name, method ) { 606 | var m = this.settings.messages[name]; 607 | return m && (m.constructor === String ? m : m[method]); 608 | }, 609 | 610 | // return the first defined argument, allowing empty strings 611 | findDefined: function() { 612 | for(var i = 0; i < arguments.length; i++) { 613 | if ( arguments[i] !== undefined ) { 614 | return arguments[i]; 615 | } 616 | } 617 | return undefined; 618 | }, 619 | 620 | defaultMessage: function( element, method ) { 621 | return this.findDefined( 622 | this.customMessage( element.name, method ), 623 | this.customDataMessage( element, method ), 624 | // title is never undefined, so handle empty string as undefined 625 | !this.settings.ignoreTitle && element.title || undefined, 626 | $.validator.messages[method], 627 | "Warning: No message defined for " + element.name + "" 628 | ); 629 | }, 630 | 631 | formatAndAdd: function( element, rule ) { 632 | var message = this.defaultMessage( element, rule.method ), 633 | theregex = /\$?\{(\d+)\}/g; 634 | if ( typeof message === "function" ) { 635 | message = message.call(this, rule.parameters, element); 636 | } else if (theregex.test(message)) { 637 | message = $.validator.format(message.replace(theregex, "{$1}"), rule.parameters); 638 | } 639 | this.errorList.push({ 640 | message: message, 641 | element: element 642 | }); 643 | 644 | this.errorMap[element.name] = message; 645 | this.submitted[element.name] = message; 646 | }, 647 | 648 | addWrapper: function( toToggle ) { 649 | if ( this.settings.wrapper ) { 650 | toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) ); 651 | } 652 | return toToggle; 653 | }, 654 | 655 | defaultShowErrors: function() { 656 | var i, elements; 657 | for ( i = 0; this.errorList[i]; i++ ) { 658 | var error = this.errorList[i]; 659 | if ( this.settings.highlight ) { 660 | this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass ); 661 | } 662 | this.showLabel( error.element, error.message ); 663 | } 664 | if ( this.errorList.length ) { 665 | this.toShow = this.toShow.add( this.containers ); 666 | } 667 | if ( this.settings.success ) { 668 | for ( i = 0; this.successList[i]; i++ ) { 669 | this.showLabel( this.successList[i] ); 670 | } 671 | } 672 | if ( this.settings.unhighlight ) { 673 | for ( i = 0, elements = this.validElements(); elements[i]; i++ ) { 674 | this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass ); 675 | } 676 | } 677 | this.toHide = this.toHide.not( this.toShow ); 678 | this.hideErrors(); 679 | this.addWrapper( this.toShow ).show(); 680 | }, 681 | 682 | validElements: function() { 683 | return this.currentElements.not(this.invalidElements()); 684 | }, 685 | 686 | invalidElements: function() { 687 | return $(this.errorList).map(function() { 688 | return this.element; 689 | }); 690 | }, 691 | 692 | showLabel: function( element, message ) { 693 | var label = this.errorsFor( element ); 694 | if ( label.length ) { 695 | // refresh error/success class 696 | label.removeClass( this.settings.validClass ).addClass( this.settings.errorClass ); 697 | // replace message on existing label 698 | label.html(message); 699 | } else { 700 | // create label 701 | label = $("<" + this.settings.errorElement + ">") 702 | .attr("for", this.idOrName(element)) 703 | .addClass(this.settings.errorClass) 704 | .html(message || ""); 705 | if ( this.settings.wrapper ) { 706 | // make sure the element is visible, even in IE 707 | // actually showing the wrapped element is handled elsewhere 708 | label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent(); 709 | } 710 | if ( !this.labelContainer.append(label).length ) { 711 | if ( this.settings.errorPlacement ) { 712 | this.settings.errorPlacement(label, $(element) ); 713 | } else { 714 | label.insertAfter(element); 715 | } 716 | } 717 | } 718 | if ( !message && this.settings.success ) { 719 | label.text(""); 720 | if ( typeof this.settings.success === "string" ) { 721 | label.addClass( this.settings.success ); 722 | } else { 723 | this.settings.success( label, element ); 724 | } 725 | } 726 | this.toShow = this.toShow.add(label); 727 | }, 728 | 729 | errorsFor: function( element ) { 730 | var name = this.idOrName(element); 731 | return this.errors().filter(function() { 732 | return $(this).attr("for") === name; 733 | }); 734 | }, 735 | 736 | idOrName: function( element ) { 737 | return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name); 738 | }, 739 | 740 | validationTargetFor: function( element ) { 741 | // if radio/checkbox, validate first element in group instead 742 | if ( this.checkable(element) ) { 743 | element = this.findByName( element.name ).not(this.settings.ignore)[0]; 744 | } 745 | return element; 746 | }, 747 | 748 | checkable: function( element ) { 749 | return (/radio|checkbox/i).test(element.type); 750 | }, 751 | 752 | findByName: function( name ) { 753 | return $(this.currentForm).find("[name='" + name + "']"); 754 | }, 755 | 756 | getLength: function( value, element ) { 757 | switch( element.nodeName.toLowerCase() ) { 758 | case "select": 759 | return $("option:selected", element).length; 760 | case "input": 761 | if ( this.checkable( element) ) { 762 | return this.findByName(element.name).filter(":checked").length; 763 | } 764 | } 765 | return value.length; 766 | }, 767 | 768 | depend: function( param, element ) { 769 | return this.dependTypes[typeof param] ? this.dependTypes[typeof param](param, element) : true; 770 | }, 771 | 772 | dependTypes: { 773 | "boolean": function( param, element ) { 774 | return param; 775 | }, 776 | "string": function( param, element ) { 777 | return !!$(param, element.form).length; 778 | }, 779 | "function": function( param, element ) { 780 | return param(element); 781 | } 782 | }, 783 | 784 | optional: function( element ) { 785 | var val = this.elementValue(element); 786 | return !$.validator.methods.required.call(this, val, element) && "dependency-mismatch"; 787 | }, 788 | 789 | startRequest: function( element ) { 790 | if ( !this.pending[element.name] ) { 791 | this.pendingRequest++; 792 | this.pending[element.name] = true; 793 | } 794 | }, 795 | 796 | stopRequest: function( element, valid ) { 797 | this.pendingRequest--; 798 | // sometimes synchronization fails, make sure pendingRequest is never < 0 799 | if ( this.pendingRequest < 0 ) { 800 | this.pendingRequest = 0; 801 | } 802 | delete this.pending[element.name]; 803 | if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) { 804 | $(this.currentForm).submit(); 805 | this.formSubmitted = false; 806 | } else if (!valid && this.pendingRequest === 0 && this.formSubmitted) { 807 | $(this.currentForm).triggerHandler("invalid-form", [this]); 808 | this.formSubmitted = false; 809 | } 810 | }, 811 | 812 | previousValue: function( element ) { 813 | return $.data(element, "previousValue") || $.data(element, "previousValue", { 814 | old: null, 815 | valid: true, 816 | message: this.defaultMessage( element, "remote" ) 817 | }); 818 | } 819 | 820 | }, 821 | 822 | classRuleSettings: { 823 | required: {required: true}, 824 | email: {email: true}, 825 | url: {url: true}, 826 | date: {date: true}, 827 | dateISO: {dateISO: true}, 828 | number: {number: true}, 829 | digits: {digits: true}, 830 | creditcard: {creditcard: true} 831 | }, 832 | 833 | addClassRules: function( className, rules ) { 834 | if ( className.constructor === String ) { 835 | this.classRuleSettings[className] = rules; 836 | } else { 837 | $.extend(this.classRuleSettings, className); 838 | } 839 | }, 840 | 841 | classRules: function( element ) { 842 | var rules = {}; 843 | var classes = $(element).attr("class"); 844 | if ( classes ) { 845 | $.each(classes.split(" "), function() { 846 | if ( this in $.validator.classRuleSettings ) { 847 | $.extend(rules, $.validator.classRuleSettings[this]); 848 | } 849 | }); 850 | } 851 | return rules; 852 | }, 853 | 854 | attributeRules: function( element ) { 855 | var rules = {}; 856 | var $element = $(element); 857 | var type = $element[0].getAttribute("type"); 858 | 859 | for (var method in $.validator.methods) { 860 | var value; 861 | 862 | // support for in both html5 and older browsers 863 | if ( method === "required" ) { 864 | value = $element.get(0).getAttribute(method); 865 | // Some browsers return an empty string for the required attribute 866 | // and non-HTML5 browsers might have required="" markup 867 | if ( value === "" ) { 868 | value = true; 869 | } 870 | // force non-HTML5 browsers to return bool 871 | value = !!value; 872 | } else { 873 | value = $element.attr(method); 874 | } 875 | 876 | // convert the value to a number for number inputs, and for text for backwards compability 877 | // allows type="date" and others to be compared as strings 878 | if ( /min|max/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) { 879 | value = Number(value); 880 | } 881 | 882 | if ( value ) { 883 | rules[method] = value; 884 | } else if ( type === method && type !== 'range' ) { 885 | // exception: the jquery validate 'range' method 886 | // does not test for the html5 'range' type 887 | rules[method] = true; 888 | } 889 | } 890 | 891 | // maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs 892 | if ( rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength) ) { 893 | delete rules.maxlength; 894 | } 895 | 896 | return rules; 897 | }, 898 | 899 | dataRules: function( element ) { 900 | var method, value, 901 | rules = {}, $element = $(element); 902 | for (method in $.validator.methods) { 903 | value = $element.data("rule-" + method.toLowerCase()); 904 | if ( value !== undefined ) { 905 | rules[method] = value; 906 | } 907 | } 908 | return rules; 909 | }, 910 | 911 | staticRules: function( element ) { 912 | var rules = {}; 913 | var validator = $.data(element.form, "validator"); 914 | if ( validator.settings.rules ) { 915 | rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {}; 916 | } 917 | return rules; 918 | }, 919 | 920 | normalizeRules: function( rules, element ) { 921 | // handle dependency check 922 | $.each(rules, function( prop, val ) { 923 | // ignore rule when param is explicitly false, eg. required:false 924 | if ( val === false ) { 925 | delete rules[prop]; 926 | return; 927 | } 928 | if ( val.param || val.depends ) { 929 | var keepRule = true; 930 | switch (typeof val.depends) { 931 | case "string": 932 | keepRule = !!$(val.depends, element.form).length; 933 | break; 934 | case "function": 935 | keepRule = val.depends.call(element, element); 936 | break; 937 | } 938 | if ( keepRule ) { 939 | rules[prop] = val.param !== undefined ? val.param : true; 940 | } else { 941 | delete rules[prop]; 942 | } 943 | } 944 | }); 945 | 946 | // evaluate parameters 947 | $.each(rules, function( rule, parameter ) { 948 | rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter; 949 | }); 950 | 951 | // clean number parameters 952 | $.each(['minlength', 'maxlength'], function() { 953 | if ( rules[this] ) { 954 | rules[this] = Number(rules[this]); 955 | } 956 | }); 957 | $.each(['rangelength', 'range'], function() { 958 | var parts; 959 | if ( rules[this] ) { 960 | if ( $.isArray(rules[this]) ) { 961 | rules[this] = [Number(rules[this][0]), Number(rules[this][1])]; 962 | } else if ( typeof rules[this] === "string" ) { 963 | parts = rules[this].split(/[\s,]+/); 964 | rules[this] = [Number(parts[0]), Number(parts[1])]; 965 | } 966 | } 967 | }); 968 | 969 | if ( $.validator.autoCreateRanges ) { 970 | // auto-create ranges 971 | if ( rules.min && rules.max ) { 972 | rules.range = [rules.min, rules.max]; 973 | delete rules.min; 974 | delete rules.max; 975 | } 976 | if ( rules.minlength && rules.maxlength ) { 977 | rules.rangelength = [rules.minlength, rules.maxlength]; 978 | delete rules.minlength; 979 | delete rules.maxlength; 980 | } 981 | } 982 | 983 | return rules; 984 | }, 985 | 986 | // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true} 987 | normalizeRule: function( data ) { 988 | if ( typeof data === "string" ) { 989 | var transformed = {}; 990 | $.each(data.split(/\s/), function() { 991 | transformed[this] = true; 992 | }); 993 | data = transformed; 994 | } 995 | return data; 996 | }, 997 | 998 | // http://docs.jquery.com/Plugins/Validation/Validator/addMethod 999 | addMethod: function( name, method, message ) { 1000 | $.validator.methods[name] = method; 1001 | $.validator.messages[name] = message !== undefined ? message : $.validator.messages[name]; 1002 | if ( method.length < 3 ) { 1003 | $.validator.addClassRules(name, $.validator.normalizeRule(name)); 1004 | } 1005 | }, 1006 | 1007 | methods: { 1008 | 1009 | // http://docs.jquery.com/Plugins/Validation/Methods/required 1010 | required: function( value, element, param ) { 1011 | // check if dependency is met 1012 | if ( !this.depend(param, element) ) { 1013 | return "dependency-mismatch"; 1014 | } 1015 | if ( element.nodeName.toLowerCase() === "select" ) { 1016 | // could be an array for select-multiple or a string, both are fine this way 1017 | var val = $(element).val(); 1018 | return val && val.length > 0; 1019 | } 1020 | if ( this.checkable(element) ) { 1021 | return this.getLength(value, element) > 0; 1022 | } 1023 | return $.trim(value).length > 0; 1024 | }, 1025 | 1026 | // http://docs.jquery.com/Plugins/Validation/Methods/email 1027 | email: function( value, element ) { 1028 | // contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/ 1029 | return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([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])))$/i.test(value); 1030 | }, 1031 | 1032 | // http://docs.jquery.com/Plugins/Validation/Methods/url 1033 | url: function( value, element ) { 1034 | // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/ 1035 | return this.optional(element) || /^(https?|s?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); 1036 | }, 1037 | 1038 | // http://docs.jquery.com/Plugins/Validation/Methods/date 1039 | date: function( value, element ) { 1040 | return this.optional(element) || !/Invalid|NaN/.test(new Date(value).toString()); 1041 | }, 1042 | 1043 | // http://docs.jquery.com/Plugins/Validation/Methods/dateISO 1044 | dateISO: function( value, element ) { 1045 | return this.optional(element) || /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(value); 1046 | }, 1047 | 1048 | // http://docs.jquery.com/Plugins/Validation/Methods/number 1049 | number: function( value, element ) { 1050 | return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value); 1051 | }, 1052 | 1053 | // http://docs.jquery.com/Plugins/Validation/Methods/digits 1054 | digits: function( value, element ) { 1055 | return this.optional(element) || /^\d+$/.test(value); 1056 | }, 1057 | 1058 | // http://docs.jquery.com/Plugins/Validation/Methods/creditcard 1059 | // based on http://en.wikipedia.org/wiki/Luhn 1060 | creditcard: function( value, element ) { 1061 | if ( this.optional(element) ) { 1062 | return "dependency-mismatch"; 1063 | } 1064 | // accept only spaces, digits and dashes 1065 | if ( /[^0-9 \-]+/.test(value) ) { 1066 | return false; 1067 | } 1068 | var nCheck = 0, 1069 | nDigit = 0, 1070 | bEven = false; 1071 | 1072 | value = value.replace(/\D/g, ""); 1073 | 1074 | for (var n = value.length - 1; n >= 0; n--) { 1075 | var cDigit = value.charAt(n); 1076 | nDigit = parseInt(cDigit, 10); 1077 | if ( bEven ) { 1078 | if ( (nDigit *= 2) > 9 ) { 1079 | nDigit -= 9; 1080 | } 1081 | } 1082 | nCheck += nDigit; 1083 | bEven = !bEven; 1084 | } 1085 | 1086 | return (nCheck % 10) === 0; 1087 | }, 1088 | 1089 | // http://docs.jquery.com/Plugins/Validation/Methods/minlength 1090 | minlength: function( value, element, param ) { 1091 | var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element); 1092 | return this.optional(element) || length >= param; 1093 | }, 1094 | 1095 | // http://docs.jquery.com/Plugins/Validation/Methods/maxlength 1096 | maxlength: function( value, element, param ) { 1097 | var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element); 1098 | return this.optional(element) || length <= param; 1099 | }, 1100 | 1101 | // http://docs.jquery.com/Plugins/Validation/Methods/rangelength 1102 | rangelength: function( value, element, param ) { 1103 | var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element); 1104 | return this.optional(element) || ( length >= param[0] && length <= param[1] ); 1105 | }, 1106 | 1107 | // http://docs.jquery.com/Plugins/Validation/Methods/min 1108 | min: function( value, element, param ) { 1109 | return this.optional(element) || value >= param; 1110 | }, 1111 | 1112 | // http://docs.jquery.com/Plugins/Validation/Methods/max 1113 | max: function( value, element, param ) { 1114 | return this.optional(element) || value <= param; 1115 | }, 1116 | 1117 | // http://docs.jquery.com/Plugins/Validation/Methods/range 1118 | range: function( value, element, param ) { 1119 | return this.optional(element) || ( value >= param[0] && value <= param[1] ); 1120 | }, 1121 | 1122 | // http://docs.jquery.com/Plugins/Validation/Methods/equalTo 1123 | equalTo: function( value, element, param ) { 1124 | // bind to the blur event of the target in order to revalidate whenever the target field is updated 1125 | // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead 1126 | var target = $(param); 1127 | if ( this.settings.onfocusout ) { 1128 | target.unbind(".validate-equalTo").bind("blur.validate-equalTo", function() { 1129 | $(element).valid(); 1130 | }); 1131 | } 1132 | return value === target.val(); 1133 | }, 1134 | 1135 | // http://docs.jquery.com/Plugins/Validation/Methods/remote 1136 | remote: function( value, element, param ) { 1137 | if ( this.optional(element) ) { 1138 | return "dependency-mismatch"; 1139 | } 1140 | 1141 | var previous = this.previousValue(element); 1142 | if (!this.settings.messages[element.name] ) { 1143 | this.settings.messages[element.name] = {}; 1144 | } 1145 | previous.originalMessage = this.settings.messages[element.name].remote; 1146 | this.settings.messages[element.name].remote = previous.message; 1147 | 1148 | param = typeof param === "string" && {url:param} || param; 1149 | 1150 | if ( previous.old === value ) { 1151 | return previous.valid; 1152 | } 1153 | 1154 | previous.old = value; 1155 | var validator = this; 1156 | this.startRequest(element); 1157 | var data = {}; 1158 | data[element.name] = value; 1159 | $.ajax($.extend(true, { 1160 | url: param, 1161 | mode: "abort", 1162 | port: "validate" + element.name, 1163 | dataType: "json", 1164 | data: data, 1165 | success: function( response ) { 1166 | validator.settings.messages[element.name].remote = previous.originalMessage; 1167 | var valid = response === true || response === "true"; 1168 | if ( valid ) { 1169 | var submitted = validator.formSubmitted; 1170 | validator.prepareElement(element); 1171 | validator.formSubmitted = submitted; 1172 | validator.successList.push(element); 1173 | delete validator.invalid[element.name]; 1174 | validator.showErrors(); 1175 | } else { 1176 | var errors = {}; 1177 | var message = response || validator.defaultMessage( element, "remote" ); 1178 | errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message; 1179 | validator.invalid[element.name] = true; 1180 | validator.showErrors(errors); 1181 | } 1182 | previous.valid = valid; 1183 | validator.stopRequest(element, valid); 1184 | } 1185 | }, param)); 1186 | return "pending"; 1187 | } 1188 | 1189 | } 1190 | 1191 | }); 1192 | 1193 | // deprecated, use $.validator.format instead 1194 | $.format = $.validator.format; 1195 | 1196 | }(jQuery)); 1197 | 1198 | // ajax mode: abort 1199 | // usage: $.ajax({ mode: "abort"[, port: "uniqueport"]}); 1200 | // if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort() 1201 | (function($) { 1202 | var pendingRequests = {}; 1203 | // Use a prefilter if available (1.5+) 1204 | if ( $.ajaxPrefilter ) { 1205 | $.ajaxPrefilter(function( settings, _, xhr ) { 1206 | var port = settings.port; 1207 | if ( settings.mode === "abort" ) { 1208 | if ( pendingRequests[port] ) { 1209 | pendingRequests[port].abort(); 1210 | } 1211 | pendingRequests[port] = xhr; 1212 | } 1213 | }); 1214 | } else { 1215 | // Proxy ajax 1216 | var ajax = $.ajax; 1217 | $.ajax = function( settings ) { 1218 | var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode, 1219 | port = ( "port" in settings ? settings : $.ajaxSettings ).port; 1220 | if ( mode === "abort" ) { 1221 | if ( pendingRequests[port] ) { 1222 | pendingRequests[port].abort(); 1223 | } 1224 | pendingRequests[port] = ajax.apply(this, arguments); 1225 | return pendingRequests[port]; 1226 | } 1227 | return ajax.apply(this, arguments); 1228 | }; 1229 | } 1230 | }(jQuery)); 1231 | 1232 | // provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation 1233 | // handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target 1234 | (function($) { 1235 | $.extend($.fn, { 1236 | validateDelegate: function( delegate, type, handler ) { 1237 | return this.bind(type, function( event ) { 1238 | var target = $(event.target); 1239 | if ( target.is(delegate) ) { 1240 | return handler.apply(target, arguments); 1241 | } 1242 | }); 1243 | } 1244 | }); 1245 | }(jQuery)); 1246 | -------------------------------------------------------------------------------- /WebApp/Scripts/jquery.validate.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! jQuery Validation Plugin - v1.11.1 - 3/22/2013\n* https://github.com/jzaefferer/jquery-validation 16 | * Copyright (c) 2013 Jörn Zaefferer; Licensed MIT */(function(t){t.extend(t.fn,{validate:function(e){if(!this.length)return e&&e.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing."),void 0;var i=t.data(this[0],"validator");return i?i:(this.attr("novalidate","novalidate"),i=new t.validator(e,this[0]),t.data(this[0],"validator",i),i.settings.onsubmit&&(this.validateDelegate(":submit","click",function(e){i.settings.submitHandler&&(i.submitButton=e.target),t(e.target).hasClass("cancel")&&(i.cancelSubmit=!0),void 0!==t(e.target).attr("formnovalidate")&&(i.cancelSubmit=!0)}),this.submit(function(e){function s(){var s;return i.settings.submitHandler?(i.submitButton&&(s=t("").attr("name",i.submitButton.name).val(t(i.submitButton).val()).appendTo(i.currentForm)),i.settings.submitHandler.call(i,i.currentForm,e),i.submitButton&&s.remove(),!1):!0}return i.settings.debug&&e.preventDefault(),i.cancelSubmit?(i.cancelSubmit=!1,s()):i.form()?i.pendingRequest?(i.formSubmitted=!0,!1):s():(i.focusInvalid(),!1)})),i)},valid:function(){if(t(this[0]).is("form"))return this.validate().form();var e=!0,i=t(this[0].form).validate();return this.each(function(){e=e&&i.element(this)}),e},removeAttrs:function(e){var i={},s=this;return t.each(e.split(/\s/),function(t,e){i[e]=s.attr(e),s.removeAttr(e)}),i},rules:function(e,i){var s=this[0];if(e){var r=t.data(s.form,"validator").settings,n=r.rules,a=t.validator.staticRules(s);switch(e){case"add":t.extend(a,t.validator.normalizeRule(i)),delete a.messages,n[s.name]=a,i.messages&&(r.messages[s.name]=t.extend(r.messages[s.name],i.messages));break;case"remove":if(!i)return delete n[s.name],a;var u={};return t.each(i.split(/\s/),function(t,e){u[e]=a[e],delete a[e]}),u}}var o=t.validator.normalizeRules(t.extend({},t.validator.classRules(s),t.validator.attributeRules(s),t.validator.dataRules(s),t.validator.staticRules(s)),s);if(o.required){var l=o.required;delete o.required,o=t.extend({required:l},o)}return o}}),t.extend(t.expr[":"],{blank:function(e){return!t.trim(""+t(e).val())},filled:function(e){return!!t.trim(""+t(e).val())},unchecked:function(e){return!t(e).prop("checked")}}),t.validator=function(e,i){this.settings=t.extend(!0,{},t.validator.defaults,e),this.currentForm=i,this.init()},t.validator.format=function(e,i){return 1===arguments.length?function(){var i=t.makeArray(arguments);return i.unshift(e),t.validator.format.apply(this,i)}:(arguments.length>2&&i.constructor!==Array&&(i=t.makeArray(arguments).slice(1)),i.constructor!==Array&&(i=[i]),t.each(i,function(t,i){e=e.replace(RegExp("\\{"+t+"\\}","g"),function(){return i})}),e)},t.extend(t.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",validClass:"valid",errorElement:"label",focusInvalid:!0,errorContainer:t([]),errorLabelContainer:t([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(t){this.lastActive=t,this.settings.focusCleanup&&!this.blockFocusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,t,this.settings.errorClass,this.settings.validClass),this.addWrapper(this.errorsFor(t)).hide())},onfocusout:function(t){this.checkable(t)||!(t.name in this.submitted)&&this.optional(t)||this.element(t)},onkeyup:function(t,e){(9!==e.which||""!==this.elementValue(t))&&(t.name in this.submitted||t===this.lastElement)&&this.element(t)},onclick:function(t){t.name in this.submitted?this.element(t):t.parentNode.name in this.submitted&&this.element(t.parentNode)},highlight:function(e,i,s){"radio"===e.type?this.findByName(e.name).addClass(i).removeClass(s):t(e).addClass(i).removeClass(s)},unhighlight:function(e,i,s){"radio"===e.type?this.findByName(e.name).removeClass(i).addClass(s):t(e).removeClass(i).addClass(s)}},setDefaults:function(e){t.extend(t.validator.defaults,e)},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.",creditcard:"Please enter a valid credit card number.",equalTo:"Please enter the same value again.",maxlength:t.validator.format("Please enter no more than {0} characters."),minlength:t.validator.format("Please enter at least {0} characters."),rangelength:t.validator.format("Please enter a value between {0} and {1} characters long."),range:t.validator.format("Please enter a value between {0} and {1}."),max:t.validator.format("Please enter a value less than or equal to {0}."),min:t.validator.format("Please enter a value greater than or equal to {0}.")},autoCreateRanges:!1,prototype:{init:function(){function e(e){var i=t.data(this[0].form,"validator"),s="on"+e.type.replace(/^validate/,"");i.settings[s]&&i.settings[s].call(i,this[0],e)}this.labelContainer=t(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||t(this.currentForm),this.containers=t(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var i=this.groups={};t.each(this.settings.groups,function(e,s){"string"==typeof s&&(s=s.split(/\s/)),t.each(s,function(t,s){i[s]=e})});var s=this.settings.rules;t.each(s,function(e,i){s[e]=t.validator.normalizeRule(i)}),t(this.currentForm).validateDelegate(":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'] ","focusin focusout keyup",e).validateDelegate("[type='radio'], [type='checkbox'], select, option","click",e),this.settings.invalidHandler&&t(this.currentForm).bind("invalid-form.validate",this.settings.invalidHandler)},form:function(){return this.checkForm(),t.extend(this.submitted,this.errorMap),this.invalid=t.extend({},this.errorMap),this.valid()||t(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var t=0,e=this.currentElements=this.elements();e[t];t++)this.check(e[t]);return this.valid()},element:function(e){e=this.validationTargetFor(this.clean(e)),this.lastElement=e,this.prepareElement(e),this.currentElements=t(e);var i=this.check(e)!==!1;return i?delete this.invalid[e.name]:this.invalid[e.name]=!0,this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),i},showErrors:function(e){if(e){t.extend(this.errorMap,e),this.errorList=[];for(var i in e)this.errorList.push({message:e[i],element:this.findByName(i)[0]});this.successList=t.grep(this.successList,function(t){return!(t.name in e)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){t.fn.resetForm&&t(this.currentForm).resetForm(),this.submitted={},this.lastElement=null,this.prepareForm(),this.hideErrors(),this.elements().removeClass(this.settings.errorClass).removeData("previousValue")},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(t){var e=0;for(var i in t)e++;return e},hideErrors:function(){this.addWrapper(this.toHide).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{t(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(e){}},findLastActive:function(){var e=this.lastActive;return e&&1===t.grep(this.errorList,function(t){return t.element.name===e.name}).length&&e},elements:function(){var e=this,i={};return t(this.currentForm).find("input, select, textarea").not(":submit, :reset, :image, [disabled]").not(this.settings.ignore).filter(function(){return!this.name&&e.settings.debug&&window.console&&console.error("%o has no name assigned",this),this.name in i||!e.objectLength(t(this).rules())?!1:(i[this.name]=!0,!0)})},clean:function(e){return t(e)[0]},errors:function(){var e=this.settings.errorClass.replace(" ",".");return t(this.settings.errorElement+"."+e,this.errorContext)},reset:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=t([]),this.toHide=t([]),this.currentElements=t([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(t){this.reset(),this.toHide=this.errorsFor(t)},elementValue:function(e){var i=t(e).attr("type"),s=t(e).val();return"radio"===i||"checkbox"===i?t("input[name='"+t(e).attr("name")+"']:checked").val():"string"==typeof s?s.replace(/\r/g,""):s},check:function(e){e=this.validationTargetFor(this.clean(e));var i,s=t(e).rules(),r=!1,n=this.elementValue(e);for(var a in s){var u={method:a,parameters:s[a]};try{if(i=t.validator.methods[a].call(this,n,e,u.parameters),"dependency-mismatch"===i){r=!0;continue}if(r=!1,"pending"===i)return this.toHide=this.toHide.not(this.errorsFor(e)),void 0;if(!i)return this.formatAndAdd(e,u),!1}catch(o){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+e.id+", check the '"+u.method+"' method.",o),o}}return r?void 0:(this.objectLength(s)&&this.successList.push(e),!0)},customDataMessage:function(e,i){return t(e).data("msg-"+i.toLowerCase())||e.attributes&&t(e).attr("data-msg-"+i.toLowerCase())},customMessage:function(t,e){var i=this.settings.messages[t];return i&&(i.constructor===String?i:i[e])},findDefined:function(){for(var t=0;arguments.length>t;t++)if(void 0!==arguments[t])return arguments[t];return void 0},defaultMessage:function(e,i){return this.findDefined(this.customMessage(e.name,i),this.customDataMessage(e,i),!this.settings.ignoreTitle&&e.title||void 0,t.validator.messages[i],"Warning: No message defined for "+e.name+"")},formatAndAdd:function(e,i){var s=this.defaultMessage(e,i.method),r=/\$?\{(\d+)\}/g;"function"==typeof s?s=s.call(this,i.parameters,e):r.test(s)&&(s=t.validator.format(s.replace(r,"{$1}"),i.parameters)),this.errorList.push({message:s,element:e}),this.errorMap[e.name]=s,this.submitted[e.name]=s},addWrapper:function(t){return this.settings.wrapper&&(t=t.add(t.parent(this.settings.wrapper))),t},defaultShowErrors:function(){var t,e;for(t=0;this.errorList[t];t++){var i=this.errorList[t];this.settings.highlight&&this.settings.highlight.call(this,i.element,this.settings.errorClass,this.settings.validClass),this.showLabel(i.element,i.message)}if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(t=0;this.successList[t];t++)this.showLabel(this.successList[t]);if(this.settings.unhighlight)for(t=0,e=this.validElements();e[t];t++)this.settings.unhighlight.call(this,e[t],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 t(this.errorList).map(function(){return this.element})},showLabel:function(e,i){var s=this.errorsFor(e);s.length?(s.removeClass(this.settings.validClass).addClass(this.settings.errorClass),s.html(i)):(s=t("<"+this.settings.errorElement+">").attr("for",this.idOrName(e)).addClass(this.settings.errorClass).html(i||""),this.settings.wrapper&&(s=s.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.append(s).length||(this.settings.errorPlacement?this.settings.errorPlacement(s,t(e)):s.insertAfter(e))),!i&&this.settings.success&&(s.text(""),"string"==typeof this.settings.success?s.addClass(this.settings.success):this.settings.success(s,e)),this.toShow=this.toShow.add(s)},errorsFor:function(e){var i=this.idOrName(e);return this.errors().filter(function(){return t(this).attr("for")===i})},idOrName:function(t){return this.groups[t.name]||(this.checkable(t)?t.name:t.id||t.name)},validationTargetFor:function(t){return this.checkable(t)&&(t=this.findByName(t.name).not(this.settings.ignore)[0]),t},checkable:function(t){return/radio|checkbox/i.test(t.type)},findByName:function(e){return t(this.currentForm).find("[name='"+e+"']")},getLength:function(e,i){switch(i.nodeName.toLowerCase()){case"select":return t("option:selected",i).length;case"input":if(this.checkable(i))return this.findByName(i.name).filter(":checked").length}return e.length},depend:function(t,e){return this.dependTypes[typeof t]?this.dependTypes[typeof t](t,e):!0},dependTypes:{"boolean":function(t){return t},string:function(e,i){return!!t(e,i.form).length},"function":function(t,e){return t(e)}},optional:function(e){var i=this.elementValue(e);return!t.validator.methods.required.call(this,i,e)&&"dependency-mismatch"},startRequest:function(t){this.pending[t.name]||(this.pendingRequest++,this.pending[t.name]=!0)},stopRequest:function(e,i){this.pendingRequest--,0>this.pendingRequest&&(this.pendingRequest=0),delete this.pending[e.name],i&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(t(this.currentForm).submit(),this.formSubmitted=!1):!i&&0===this.pendingRequest&&this.formSubmitted&&(t(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(e){return t.data(e,"previousValue")||t.data(e,"previousValue",{old:null,valid:!0,message:this.defaultMessage(e,"remote")})}},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(e,i){e.constructor===String?this.classRuleSettings[e]=i:t.extend(this.classRuleSettings,e)},classRules:function(e){var i={},s=t(e).attr("class");return s&&t.each(s.split(" "),function(){this in t.validator.classRuleSettings&&t.extend(i,t.validator.classRuleSettings[this])}),i},attributeRules:function(e){var i={},s=t(e),r=s[0].getAttribute("type");for(var n in t.validator.methods){var a;"required"===n?(a=s.get(0).getAttribute(n),""===a&&(a=!0),a=!!a):a=s.attr(n),/min|max/.test(n)&&(null===r||/number|range|text/.test(r))&&(a=Number(a)),a?i[n]=a:r===n&&"range"!==r&&(i[n]=!0)}return i.maxlength&&/-1|2147483647|524288/.test(i.maxlength)&&delete i.maxlength,i},dataRules:function(e){var i,s,r={},n=t(e);for(i in t.validator.methods)s=n.data("rule-"+i.toLowerCase()),void 0!==s&&(r[i]=s);return r},staticRules:function(e){var i={},s=t.data(e.form,"validator");return s.settings.rules&&(i=t.validator.normalizeRule(s.settings.rules[e.name])||{}),i},normalizeRules:function(e,i){return t.each(e,function(s,r){if(r===!1)return delete e[s],void 0;if(r.param||r.depends){var n=!0;switch(typeof r.depends){case"string":n=!!t(r.depends,i.form).length;break;case"function":n=r.depends.call(i,i)}n?e[s]=void 0!==r.param?r.param:!0:delete e[s]}}),t.each(e,function(s,r){e[s]=t.isFunction(r)?r(i):r}),t.each(["minlength","maxlength"],function(){e[this]&&(e[this]=Number(e[this]))}),t.each(["rangelength","range"],function(){var i;e[this]&&(t.isArray(e[this])?e[this]=[Number(e[this][0]),Number(e[this][1])]:"string"==typeof e[this]&&(i=e[this].split(/[\s,]+/),e[this]=[Number(i[0]),Number(i[1])]))}),t.validator.autoCreateRanges&&(e.min&&e.max&&(e.range=[e.min,e.max],delete e.min,delete e.max),e.minlength&&e.maxlength&&(e.rangelength=[e.minlength,e.maxlength],delete e.minlength,delete e.maxlength)),e},normalizeRule:function(e){if("string"==typeof e){var i={};t.each(e.split(/\s/),function(){i[this]=!0}),e=i}return e},addMethod:function(e,i,s){t.validator.methods[e]=i,t.validator.messages[e]=void 0!==s?s:t.validator.messages[e],3>i.length&&t.validator.addClassRules(e,t.validator.normalizeRule(e))},methods:{required:function(e,i,s){if(!this.depend(s,i))return"dependency-mismatch";if("select"===i.nodeName.toLowerCase()){var r=t(i).val();return r&&r.length>0}return this.checkable(i)?this.getLength(e,i)>0:t.trim(e).length>0},email:function(t,e){return this.optional(e)||/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([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])))$/i.test(t)},url:function(t,e){return this.optional(e)||/^(https?|s?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(t)},date:function(t,e){return this.optional(e)||!/Invalid|NaN/.test(""+new Date(t))},dateISO:function(t,e){return this.optional(e)||/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(t)},number:function(t,e){return this.optional(e)||/^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(t)},digits:function(t,e){return this.optional(e)||/^\d+$/.test(t)},creditcard:function(t,e){if(this.optional(e))return"dependency-mismatch";if(/[^0-9 \-]+/.test(t))return!1;var i=0,s=0,r=!1;t=t.replace(/\D/g,"");for(var n=t.length-1;n>=0;n--){var a=t.charAt(n);s=parseInt(a,10),r&&(s*=2)>9&&(s-=9),i+=s,r=!r}return 0===i%10},minlength:function(e,i,s){var r=t.isArray(e)?e.length:this.getLength(t.trim(e),i);return this.optional(i)||r>=s},maxlength:function(e,i,s){var r=t.isArray(e)?e.length:this.getLength(t.trim(e),i);return this.optional(i)||s>=r},rangelength:function(e,i,s){var r=t.isArray(e)?e.length:this.getLength(t.trim(e),i);return this.optional(i)||r>=s[0]&&s[1]>=r},min:function(t,e,i){return this.optional(e)||t>=i},max:function(t,e,i){return this.optional(e)||i>=t},range:function(t,e,i){return this.optional(e)||t>=i[0]&&i[1]>=t},equalTo:function(e,i,s){var r=t(s);return this.settings.onfocusout&&r.unbind(".validate-equalTo").bind("blur.validate-equalTo",function(){t(i).valid()}),e===r.val()},remote:function(e,i,s){if(this.optional(i))return"dependency-mismatch";var r=this.previousValue(i);if(this.settings.messages[i.name]||(this.settings.messages[i.name]={}),r.originalMessage=this.settings.messages[i.name].remote,this.settings.messages[i.name].remote=r.message,s="string"==typeof s&&{url:s}||s,r.old===e)return r.valid;r.old=e;var n=this;this.startRequest(i);var a={};return a[i.name]=e,t.ajax(t.extend(!0,{url:s,mode:"abort",port:"validate"+i.name,dataType:"json",data:a,success:function(s){n.settings.messages[i.name].remote=r.originalMessage;var a=s===!0||"true"===s;if(a){var u=n.formSubmitted;n.prepareElement(i),n.formSubmitted=u,n.successList.push(i),delete n.invalid[i.name],n.showErrors()}else{var o={},l=s||n.defaultMessage(i,"remote");o[i.name]=r.message=t.isFunction(l)?l(e):l,n.invalid[i.name]=!0,n.showErrors(o)}r.valid=a,n.stopRequest(i,a)}},s)),"pending"}}}),t.format=t.validator.format})(jQuery),function(t){var e={};if(t.ajaxPrefilter)t.ajaxPrefilter(function(t,i,s){var r=t.port;"abort"===t.mode&&(e[r]&&e[r].abort(),e[r]=s)});else{var i=t.ajax;t.ajax=function(s){var r=("mode"in s?s:t.ajaxSettings).mode,n=("port"in s?s:t.ajaxSettings).port;return"abort"===r?(e[n]&&e[n].abort(),e[n]=i.apply(this,arguments),e[n]):i.apply(this,arguments)}}}(jQuery),function(t){t.extend(t.fn,{validateDelegate:function(e,i,s){return this.bind(i,function(i){var r=t(i.target);return r.is(e)?s.apply(r,arguments):void 0})}})}(jQuery); -------------------------------------------------------------------------------- /WebApp/Scripts/jquery.validate.unobtrusive.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! 16 | ** Unobtrusive validation support library for jQuery and jQuery Validate 17 | ** Copyright (C) Microsoft Corporation. All rights reserved. 18 | */ 19 | 20 | /*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 */ 21 | /*global document: false, jQuery: false */ 22 | 23 | (function ($) { 24 | var $jQval = $.validator, 25 | adapters, 26 | data_validation = "unobtrusiveValidation"; 27 | 28 | function setValidationValues(options, ruleName, value) { 29 | options.rules[ruleName] = value; 30 | if (options.message) { 31 | options.messages[ruleName] = options.message; 32 | } 33 | } 34 | 35 | function splitAndTrim(value) { 36 | return value.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/g); 37 | } 38 | 39 | function escapeAttributeValue(value) { 40 | // As mentioned on http://api.jquery.com/category/selectors/ 41 | return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1"); 42 | } 43 | 44 | function getModelPrefix(fieldName) { 45 | return fieldName.substr(0, fieldName.lastIndexOf(".") + 1); 46 | } 47 | 48 | function appendModelPrefix(value, prefix) { 49 | if (value.indexOf("*.") === 0) { 50 | value = value.replace("*.", prefix); 51 | } 52 | return value; 53 | } 54 | 55 | function onError(error, inputElement) { // 'this' is the form element 56 | var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"), 57 | replaceAttrValue = container.attr("data-valmsg-replace"), 58 | replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null; 59 | 60 | container.removeClass("field-validation-valid").addClass("field-validation-error"); 61 | error.data("unobtrusiveContainer", container); 62 | 63 | if (replace) { 64 | container.empty(); 65 | error.removeClass("input-validation-error").appendTo(container); 66 | } 67 | else { 68 | error.hide(); 69 | } 70 | } 71 | 72 | function onErrors(event, validator) { // 'this' is the form element 73 | var container = $(this).find("[data-valmsg-summary=true]"), 74 | list = container.find("ul"); 75 | 76 | if (list && list.length && validator.errorList.length) { 77 | list.empty(); 78 | container.addClass("validation-summary-errors").removeClass("validation-summary-valid"); 79 | 80 | $.each(validator.errorList, function () { 81 | $("
  • ").html(this.message).appendTo(list); 82 | }); 83 | } 84 | } 85 | 86 | function onSuccess(error) { // 'this' is the form element 87 | var container = error.data("unobtrusiveContainer"), 88 | replaceAttrValue = container.attr("data-valmsg-replace"), 89 | replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null; 90 | 91 | if (container) { 92 | container.addClass("field-validation-valid").removeClass("field-validation-error"); 93 | error.removeData("unobtrusiveContainer"); 94 | 95 | if (replace) { 96 | container.empty(); 97 | } 98 | } 99 | } 100 | 101 | function onReset(event) { // 'this' is the form element 102 | var $form = $(this), 103 | key = '__jquery_unobtrusive_validation_form_reset'; 104 | if ($form.data(key)) { 105 | return; 106 | } 107 | // Set a flag that indicates we're currently resetting the form. 108 | $form.data(key, true); 109 | try { 110 | $form.data("validator").resetForm(); 111 | } finally { 112 | $form.removeData(key); 113 | } 114 | 115 | $form.find(".validation-summary-errors") 116 | .addClass("validation-summary-valid") 117 | .removeClass("validation-summary-errors"); 118 | $form.find(".field-validation-error") 119 | .addClass("field-validation-valid") 120 | .removeClass("field-validation-error") 121 | .removeData("unobtrusiveContainer") 122 | .find(">*") // If we were using valmsg-replace, get the underlying error 123 | .removeData("unobtrusiveContainer"); 124 | } 125 | 126 | function validationInfo(form) { 127 | var $form = $(form), 128 | result = $form.data(data_validation), 129 | onResetProxy = $.proxy(onReset, form), 130 | defaultOptions = $jQval.unobtrusive.options || {}, 131 | execInContext = function (name, args) { 132 | var func = defaultOptions[name]; 133 | func && $.isFunction(func) && func.apply(form, args); 134 | } 135 | 136 | if (!result) { 137 | result = { 138 | options: { // options structure passed to jQuery Validate's validate() method 139 | errorClass: defaultOptions.errorClass || "input-validation-error", 140 | errorElement: defaultOptions.errorElement || "span", 141 | errorPlacement: function () { 142 | onError.apply(form, arguments); 143 | execInContext("errorPlacement", arguments); 144 | }, 145 | invalidHandler: function () { 146 | onErrors.apply(form, arguments); 147 | execInContext("invalidHandler", arguments); 148 | }, 149 | messages: {}, 150 | rules: {}, 151 | success: function () { 152 | onSuccess.apply(form, arguments); 153 | execInContext("success", arguments); 154 | } 155 | }, 156 | attachValidation: function () { 157 | $form 158 | .off("reset." + data_validation, onResetProxy) 159 | .on("reset." + data_validation, onResetProxy) 160 | .validate(this.options); 161 | }, 162 | validate: function () { // a validation function that is called by unobtrusive Ajax 163 | $form.validate(); 164 | return $form.valid(); 165 | } 166 | }; 167 | $form.data(data_validation, result); 168 | } 169 | 170 | return result; 171 | } 172 | 173 | $jQval.unobtrusive = { 174 | adapters: [], 175 | 176 | parseElement: function (element, skipAttach) { 177 | /// 178 | /// Parses a single HTML element for unobtrusive validation attributes. 179 | /// 180 | /// The HTML element to be parsed. 181 | /// [Optional] true to skip attaching the 182 | /// validation to the form. If parsing just this single element, you should specify true. 183 | /// If parsing several elements, you should specify false, and manually attach the validation 184 | /// to the form when you are finished. The default is false. 185 | var $element = $(element), 186 | form = $element.parents("form")[0], 187 | valInfo, rules, messages; 188 | 189 | if (!form) { // Cannot do client-side validation without a form 190 | return; 191 | } 192 | 193 | valInfo = validationInfo(form); 194 | valInfo.options.rules[element.name] = rules = {}; 195 | valInfo.options.messages[element.name] = messages = {}; 196 | 197 | $.each(this.adapters, function () { 198 | var prefix = "data-val-" + this.name, 199 | message = $element.attr(prefix), 200 | paramValues = {}; 201 | 202 | if (message !== undefined) { // Compare against undefined, because an empty message is legal (and falsy) 203 | prefix += "-"; 204 | 205 | $.each(this.params, function () { 206 | paramValues[this] = $element.attr(prefix + this); 207 | }); 208 | 209 | this.adapt({ 210 | element: element, 211 | form: form, 212 | message: message, 213 | params: paramValues, 214 | rules: rules, 215 | messages: messages 216 | }); 217 | } 218 | }); 219 | 220 | $.extend(rules, { "__dummy__": true }); 221 | 222 | if (!skipAttach) { 223 | valInfo.attachValidation(); 224 | } 225 | }, 226 | 227 | parse: function (selector) { 228 | /// 229 | /// Parses all the HTML elements in the specified selector. It looks for input elements decorated 230 | /// with the [data-val=true] attribute value and enables validation according to the data-val-* 231 | /// attribute values. 232 | /// 233 | /// Any valid jQuery selector. 234 | 235 | // $forms includes all forms in selector's DOM hierarchy (parent, children and self) that have at least one 236 | // element with data-val=true 237 | var $selector = $(selector), 238 | $forms = $selector.parents() 239 | .addBack() 240 | .filter("form") 241 | .add($selector.find("form")) 242 | .has("[data-val=true]"); 243 | 244 | $selector.find("[data-val=true]").each(function () { 245 | $jQval.unobtrusive.parseElement(this, true); 246 | }); 247 | 248 | $forms.each(function () { 249 | var info = validationInfo(this); 250 | if (info) { 251 | info.attachValidation(); 252 | } 253 | }); 254 | } 255 | }; 256 | 257 | adapters = $jQval.unobtrusive.adapters; 258 | 259 | adapters.add = function (adapterName, params, fn) { 260 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation. 261 | /// The name of the adapter to be added. This matches the name used 262 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 263 | /// [Optional] An array of parameter names (strings) that will 264 | /// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and 265 | /// mmmm is the parameter name). 266 | /// The function to call, which adapts the values from the HTML 267 | /// attributes into jQuery Validate rules and/or messages. 268 | /// 269 | if (!fn) { // Called with no params, just a function 270 | fn = params; 271 | params = []; 272 | } 273 | this.push({ name: adapterName, params: params, adapt: fn }); 274 | return this; 275 | }; 276 | 277 | adapters.addBool = function (adapterName, ruleName) { 278 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 279 | /// the jQuery Validate validation rule has no parameter values. 280 | /// The name of the adapter to be added. This matches the name used 281 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 282 | /// [Optional] The name of the jQuery Validate rule. If not provided, the value 283 | /// of adapterName will be used instead. 284 | /// 285 | return this.add(adapterName, function (options) { 286 | setValidationValues(options, ruleName || adapterName, true); 287 | }); 288 | }; 289 | 290 | adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) { 291 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 292 | /// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and 293 | /// one for min-and-max). The HTML parameters are expected to be named -min and -max. 294 | /// The name of the adapter to be added. This matches the name used 295 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 296 | /// The name of the jQuery Validate rule to be used when you only 297 | /// have a minimum value. 298 | /// The name of the jQuery Validate rule to be used when you only 299 | /// have a maximum value. 300 | /// The name of the jQuery Validate rule to be used when you 301 | /// have both a minimum and maximum value. 302 | /// [Optional] The name of the HTML attribute that 303 | /// contains the minimum value. The default is "min". 304 | /// [Optional] The name of the HTML attribute that 305 | /// contains the maximum value. The default is "max". 306 | /// 307 | return this.add(adapterName, [minAttribute || "min", maxAttribute || "max"], function (options) { 308 | var min = options.params.min, 309 | max = options.params.max; 310 | 311 | if (min && max) { 312 | setValidationValues(options, minMaxRuleName, [min, max]); 313 | } 314 | else if (min) { 315 | setValidationValues(options, minRuleName, min); 316 | } 317 | else if (max) { 318 | setValidationValues(options, maxRuleName, max); 319 | } 320 | }); 321 | }; 322 | 323 | adapters.addSingleVal = function (adapterName, attribute, ruleName) { 324 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 325 | /// the jQuery Validate validation rule has a single value. 326 | /// The name of the adapter to be added. This matches the name used 327 | /// in the data-val-nnnn HTML attribute(where nnnn is the adapter name). 328 | /// [Optional] The name of the HTML attribute that contains the value. 329 | /// The default is "val". 330 | /// [Optional] The name of the jQuery Validate rule. If not provided, the value 331 | /// of adapterName will be used instead. 332 | /// 333 | return this.add(adapterName, [attribute || "val"], function (options) { 334 | setValidationValues(options, ruleName || adapterName, options.params[attribute]); 335 | }); 336 | }; 337 | 338 | $jQval.addMethod("__dummy__", function (value, element, params) { 339 | return true; 340 | }); 341 | 342 | $jQval.addMethod("regex", function (value, element, params) { 343 | var match; 344 | if (this.optional(element)) { 345 | return true; 346 | } 347 | 348 | match = new RegExp(params).exec(value); 349 | return (match && (match.index === 0) && (match[0].length === value.length)); 350 | }); 351 | 352 | $jQval.addMethod("nonalphamin", function (value, element, nonalphamin) { 353 | var match; 354 | if (nonalphamin) { 355 | match = value.match(/\W/g); 356 | match = match && match.length >= nonalphamin; 357 | } 358 | return match; 359 | }); 360 | 361 | if ($jQval.methods.extension) { 362 | adapters.addSingleVal("accept", "mimtype"); 363 | adapters.addSingleVal("extension", "extension"); 364 | } else { 365 | // for backward compatibility, when the 'extension' validation method does not exist, such as with versions 366 | // of JQuery Validation plugin prior to 1.10, we should use the 'accept' method for 367 | // validating the extension, and ignore mime-type validations as they are not supported. 368 | adapters.addSingleVal("extension", "extension", "accept"); 369 | } 370 | 371 | adapters.addSingleVal("regex", "pattern"); 372 | adapters.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"); 373 | adapters.addMinMax("length", "minlength", "maxlength", "rangelength").addMinMax("range", "min", "max", "range"); 374 | adapters.addMinMax("minlength", "minlength").addMinMax("maxlength", "minlength", "maxlength"); 375 | adapters.add("equalto", ["other"], function (options) { 376 | var prefix = getModelPrefix(options.element.name), 377 | other = options.params.other, 378 | fullOtherName = appendModelPrefix(other, prefix), 379 | element = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(fullOtherName) + "']")[0]; 380 | 381 | setValidationValues(options, "equalTo", element); 382 | }); 383 | adapters.add("required", function (options) { 384 | // jQuery Validate equates "required" with "mandatory" for checkbox elements 385 | if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") { 386 | setValidationValues(options, "required", true); 387 | } 388 | }); 389 | adapters.add("remote", ["url", "type", "additionalfields"], function (options) { 390 | var value = { 391 | url: options.params.url, 392 | type: options.params.type || "GET", 393 | data: {} 394 | }, 395 | prefix = getModelPrefix(options.element.name); 396 | 397 | $.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) { 398 | var paramName = appendModelPrefix(fieldName, prefix); 399 | value.data[paramName] = function () { 400 | var field = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(paramName) + "']"); 401 | // For checkboxes and radio buttons, only pick up values from checked fields. 402 | if (field.is(":checkbox")) { 403 | return field.filter(":checked").val() || field.filter(":hidden").val() || ''; 404 | } 405 | else if (field.is(":radio")) { 406 | return field.filter(":checked").val() || ''; 407 | } 408 | return field.val(); 409 | }; 410 | }); 411 | 412 | setValidationValues(options, "remote", value); 413 | }); 414 | adapters.add("password", ["min", "nonalphamin", "regex"], function (options) { 415 | if (options.params.min) { 416 | setValidationValues(options, "minlength", options.params.min); 417 | } 418 | if (options.params.nonalphamin) { 419 | setValidationValues(options, "nonalphamin", options.params.nonalphamin); 420 | } 421 | if (options.params.regex) { 422 | setValidationValues(options, "regex", options.params.regex); 423 | } 424 | }); 425 | 426 | $(function () { 427 | $jQval.unobtrusive.parse(document); 428 | }); 429 | }(jQuery)); -------------------------------------------------------------------------------- /WebApp/Scripts/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /* 16 | ** Unobtrusive validation support library for jQuery and jQuery Validate 17 | ** Copyright (C) Microsoft Corporation. All rights reserved. 18 | */ 19 | (function(a){var d=a.validator,b,e="unobtrusiveValidation";function c(a,b,c){a.rules[b]=c;if(a.message)a.messages[b]=a.message}function j(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function f(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function h(a){return a.substr(0,a.lastIndexOf(".")+1)}function g(a,b){if(a.indexOf("*.")===0)a=a.replace("*.",b);return a}function m(c,e){var b=a(this).find("[data-valmsg-for='"+f(e[0].name)+"']"),d=b.attr("data-valmsg-replace"),g=d?a.parseJSON(d)!==false:null;b.removeClass("field-validation-valid").addClass("field-validation-error");c.data("unobtrusiveContainer",b);if(g){b.empty();c.removeClass("input-validation-error").appendTo(b)}else c.hide()}function l(e,d){var c=a(this).find("[data-valmsg-summary=true]"),b=c.find("ul");if(b&&b.length&&d.errorList.length){b.empty();c.addClass("validation-summary-errors").removeClass("validation-summary-valid");a.each(d.errorList,function(){a("
  • ").html(this.message).appendTo(b)})}}function k(d){var b=d.data("unobtrusiveContainer"),c=b.attr("data-valmsg-replace"),e=c?a.parseJSON(c):null;if(b){b.addClass("field-validation-valid").removeClass("field-validation-error");d.removeData("unobtrusiveContainer");e&&b.empty()}}function n(){var b=a(this),c="__jquery_unobtrusive_validation_form_reset";if(b.data(c))return;b.data(c,true);try{b.data("validator").resetForm()}finally{b.removeData(c)}b.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors");b.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}function i(b){var c=a(b),f=c.data(e),i=a.proxy(n,b),g=d.unobtrusive.options||{},h=function(e,d){var c=g[e];c&&a.isFunction(c)&&c.apply(b,d)};if(!f){f={options:{errorClass:g.errorClass||"input-validation-error",errorElement:g.errorElement||"span",errorPlacement:function(){m.apply(b,arguments);h("errorPlacement",arguments)},invalidHandler:function(){l.apply(b,arguments);h("invalidHandler",arguments)},messages:{},rules:{},success:function(){k.apply(b,arguments);h("success",arguments)}},attachValidation:function(){c.off("reset."+e,i).on("reset."+e,i).validate(this.options)},validate:function(){c.validate();return c.valid()}};c.data(e,f)}return f}d.unobtrusive={adapters:[],parseElement:function(b,h){var d=a(b),f=d.parents("form")[0],c,e,g;if(!f)return;c=i(f);c.options.rules[b.name]=e={};c.options.messages[b.name]=g={};a.each(this.adapters,function(){var c="data-val-"+this.name,i=d.attr(c),h={};if(i!==undefined){c+="-";a.each(this.params,function(){h[this]=d.attr(c+this)});this.adapt({element:b,form:f,message:i,params:h,rules:e,messages:g})}});a.extend(e,{__dummy__:true});!h&&c.attachValidation()},parse:function(c){var b=a(c),e=b.parents().addBack().filter("form").add(b.find("form")).has("[data-val=true]");b.find("[data-val=true]").each(function(){d.unobtrusive.parseElement(this,true)});e.each(function(){var a=i(this);a&&a.attachValidation()})}};b=d.unobtrusive.adapters;b.add=function(c,a,b){if(!b){b=a;a=[]}this.push({name:c,params:a,adapt:b});return this};b.addBool=function(a,b){return this.add(a,function(d){c(d,b||a,true)})};b.addMinMax=function(e,g,f,a,d,b){return this.add(e,[d||"min",b||"max"],function(b){var e=b.params.min,d=b.params.max;if(e&&d)c(b,a,[e,d]);else if(e)c(b,g,e);else d&&c(b,f,d)})};b.addSingleVal=function(a,b,d){return this.add(a,[b||"val"],function(e){c(e,d||a,e.params[b])})};d.addMethod("__dummy__",function(){return true});d.addMethod("regex",function(b,c,d){var a;if(this.optional(c))return true;a=(new RegExp(d)).exec(b);return a&&a.index===0&&a[0].length===b.length});d.addMethod("nonalphamin",function(c,d,b){var a;if(b){a=c.match(/\W/g);a=a&&a.length>=b}return a});if(d.methods.extension){b.addSingleVal("accept","mimtype");b.addSingleVal("extension","extension")}else b.addSingleVal("extension","extension","accept");b.addSingleVal("regex","pattern");b.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");b.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range");b.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength");b.add("equalto",["other"],function(b){var i=h(b.element.name),j=b.params.other,d=g(j,i),e=a(b.form).find(":input").filter("[name='"+f(d)+"']")[0];c(b,"equalTo",e)});b.add("required",function(a){(a.element.tagName.toUpperCase()!=="INPUT"||a.element.type.toUpperCase()!=="CHECKBOX")&&c(a,"required",true)});b.add("remote",["url","type","additionalfields"],function(b){var d={url:b.params.url,type:b.params.type||"GET",data:{}},e=h(b.element.name);a.each(j(b.params.additionalfields||b.element.name),function(i,h){var c=g(h,e);d.data[c]=function(){var d=a(b.form).find(":input").filter("[name='"+f(c)+"']");return d.is(":checkbox")?d.filter(":checked").val()||d.filter(":hidden").val()||"":d.is(":radio")?d.filter(":checked").val()||"":d.val()}});c(b,"remote",d)});b.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&c(a,"minlength",a.params.min);a.params.nonalphamin&&c(a,"nonalphamin",a.params.nonalphamin);a.params.regex&&c(a,"regex",a.params.regex)});a(function(){d.unobtrusive.parse(document)})})(jQuery); -------------------------------------------------------------------------------- /WebApp/Scripts/respond.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ 16 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ 17 | window.matchMedia = window.matchMedia || (function(doc, undefined){ 18 | 19 | var bool, 20 | docElem = doc.documentElement, 21 | refNode = docElem.firstElementChild || docElem.firstChild, 22 | // fakeBody required for 23 | fakeBody = doc.createElement('body'), 24 | div = doc.createElement('div'); 25 | 26 | div.id = 'mq-test-1'; 27 | div.style.cssText = "position:absolute;top:-100em"; 28 | fakeBody.style.background = "none"; 29 | fakeBody.appendChild(div); 30 | 31 | return function(q){ 32 | 33 | div.innerHTML = '­'; 34 | 35 | docElem.insertBefore(fakeBody, refNode); 36 | bool = div.offsetWidth == 42; 37 | docElem.removeChild(fakeBody); 38 | 39 | return { matches: bool, media: q }; 40 | }; 41 | 42 | })(document); 43 | 44 | 45 | 46 | 47 | /*! Respond.js v1.2.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 48 | (function( win ){ 49 | //exposed namespace 50 | win.respond = {}; 51 | 52 | //define update even in native-mq-supporting browsers, to avoid errors 53 | respond.update = function(){}; 54 | 55 | //expose media query support flag for external use 56 | respond.mediaQueriesSupported = win.matchMedia && win.matchMedia( "only all" ).matches; 57 | 58 | //if media queries are supported, exit here 59 | if( respond.mediaQueriesSupported ){ return; } 60 | 61 | //define vars 62 | var doc = win.document, 63 | docElem = doc.documentElement, 64 | mediastyles = [], 65 | rules = [], 66 | appendedEls = [], 67 | parsedSheets = {}, 68 | resizeThrottle = 30, 69 | head = doc.getElementsByTagName( "head" )[0] || docElem, 70 | base = doc.getElementsByTagName( "base" )[0], 71 | links = head.getElementsByTagName( "link" ), 72 | requestQueue = [], 73 | 74 | //loop stylesheets, send text content to translate 75 | ripCSS = function(){ 76 | var sheets = links, 77 | sl = sheets.length, 78 | i = 0, 79 | //vars for loop: 80 | sheet, href, media, isCSS; 81 | 82 | for( ; i < sl; i++ ){ 83 | sheet = sheets[ i ], 84 | href = sheet.href, 85 | media = sheet.media, 86 | isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet"; 87 | 88 | //only links plz and prevent re-parsing 89 | if( !!href && isCSS && !parsedSheets[ href ] ){ 90 | // selectivizr exposes css through the rawCssText expando 91 | if (sheet.styleSheet && sheet.styleSheet.rawCssText) { 92 | translate( sheet.styleSheet.rawCssText, href, media ); 93 | parsedSheets[ href ] = true; 94 | } else { 95 | if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base) 96 | || href.replace( RegExp.$1, "" ).split( "/" )[0] === win.location.host ){ 97 | requestQueue.push( { 98 | href: href, 99 | media: media 100 | } ); 101 | } 102 | } 103 | } 104 | } 105 | makeRequests(); 106 | }, 107 | 108 | //recurse through request queue, get css text 109 | makeRequests = function(){ 110 | if( requestQueue.length ){ 111 | var thisRequest = requestQueue.shift(); 112 | 113 | ajax( thisRequest.href, function( styles ){ 114 | translate( styles, thisRequest.href, thisRequest.media ); 115 | parsedSheets[ thisRequest.href ] = true; 116 | makeRequests(); 117 | } ); 118 | } 119 | }, 120 | 121 | //find media blocks in css text, convert to style blocks 122 | translate = function( styles, href, media ){ 123 | var qs = styles.match( /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi ), 124 | ql = qs && qs.length || 0, 125 | //try to get CSS path 126 | href = href.substring( 0, href.lastIndexOf( "/" )), 127 | repUrls = function( css ){ 128 | return css.replace( /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, "$1" + href + "$2$3" ); 129 | }, 130 | useMedia = !ql && media, 131 | //vars used in loop 132 | i = 0, 133 | j, fullq, thisq, eachq, eql; 134 | 135 | //if path exists, tack on trailing slash 136 | if( href.length ){ href += "/"; } 137 | 138 | //if no internal queries exist, but media attr does, use that 139 | //note: this currently lacks support for situations where a media attr is specified on a link AND 140 | //its associated stylesheet has internal CSS media queries. 141 | //In those cases, the media attribute will currently be ignored. 142 | if( useMedia ){ 143 | ql = 1; 144 | } 145 | 146 | 147 | for( ; i < ql; i++ ){ 148 | j = 0; 149 | 150 | //media attr 151 | if( useMedia ){ 152 | fullq = media; 153 | rules.push( repUrls( styles ) ); 154 | } 155 | //parse for styles 156 | else{ 157 | fullq = qs[ i ].match( /@media *([^\{]+)\{([\S\s]+?)$/ ) && RegExp.$1; 158 | rules.push( RegExp.$2 && repUrls( RegExp.$2 ) ); 159 | } 160 | 161 | eachq = fullq.split( "," ); 162 | eql = eachq.length; 163 | 164 | for( ; j < eql; j++ ){ 165 | thisq = eachq[ j ]; 166 | mediastyles.push( { 167 | media : thisq.split( "(" )[ 0 ].match( /(only\s+)?([a-zA-Z]+)\s?/ ) && RegExp.$2 || "all", 168 | rules : rules.length - 1, 169 | hasquery: thisq.indexOf("(") > -1, 170 | minw : thisq.match( /\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ), 171 | maxw : thisq.match( /\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ) 172 | } ); 173 | } 174 | } 175 | 176 | applyMedia(); 177 | }, 178 | 179 | lastCall, 180 | 181 | resizeDefer, 182 | 183 | // returns the value of 1em in pixels 184 | getEmValue = function() { 185 | var ret, 186 | div = doc.createElement('div'), 187 | body = doc.body, 188 | fakeUsed = false; 189 | 190 | div.style.cssText = "position:absolute;font-size:1em;width:1em"; 191 | 192 | if( !body ){ 193 | body = fakeUsed = doc.createElement( "body" ); 194 | body.style.background = "none"; 195 | } 196 | 197 | body.appendChild( div ); 198 | 199 | docElem.insertBefore( body, docElem.firstChild ); 200 | 201 | ret = div.offsetWidth; 202 | 203 | if( fakeUsed ){ 204 | docElem.removeChild( body ); 205 | } 206 | else { 207 | body.removeChild( div ); 208 | } 209 | 210 | //also update eminpx before returning 211 | ret = eminpx = parseFloat(ret); 212 | 213 | return ret; 214 | }, 215 | 216 | //cached container for 1em value, populated the first time it's needed 217 | eminpx, 218 | 219 | //enable/disable styles 220 | applyMedia = function( fromResize ){ 221 | var name = "clientWidth", 222 | docElemProp = docElem[ name ], 223 | currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp, 224 | styleBlocks = {}, 225 | lastLink = links[ links.length-1 ], 226 | now = (new Date()).getTime(); 227 | 228 | //throttle resize calls 229 | if( fromResize && lastCall && now - lastCall < resizeThrottle ){ 230 | clearTimeout( resizeDefer ); 231 | resizeDefer = setTimeout( applyMedia, resizeThrottle ); 232 | return; 233 | } 234 | else { 235 | lastCall = now; 236 | } 237 | 238 | for( var i in mediastyles ){ 239 | var thisstyle = mediastyles[ i ], 240 | min = thisstyle.minw, 241 | max = thisstyle.maxw, 242 | minnull = min === null, 243 | maxnull = max === null, 244 | em = "em"; 245 | 246 | if( !!min ){ 247 | min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); 248 | } 249 | if( !!max ){ 250 | max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); 251 | } 252 | 253 | // if there's no media query at all (the () part), or min or max is not null, and if either is present, they're true 254 | if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){ 255 | if( !styleBlocks[ thisstyle.media ] ){ 256 | styleBlocks[ thisstyle.media ] = []; 257 | } 258 | styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] ); 259 | } 260 | } 261 | 262 | //remove any existing respond style element(s) 263 | for( var i in appendedEls ){ 264 | if( appendedEls[ i ] && appendedEls[ i ].parentNode === head ){ 265 | head.removeChild( appendedEls[ i ] ); 266 | } 267 | } 268 | 269 | //inject active styles, grouped by media type 270 | for( var i in styleBlocks ){ 271 | var ss = doc.createElement( "style" ), 272 | css = styleBlocks[ i ].join( "\n" ); 273 | 274 | ss.type = "text/css"; 275 | ss.media = i; 276 | 277 | //originally, ss was appended to a documentFragment and sheets were appended in bulk. 278 | //this caused crashes in IE in a number of circumstances, such as when the HTML element had a bg image set, so appending beforehand seems best. Thanks to @dvelyk for the initial research on this one! 279 | head.insertBefore( ss, lastLink.nextSibling ); 280 | 281 | if ( ss.styleSheet ){ 282 | ss.styleSheet.cssText = css; 283 | } 284 | else { 285 | ss.appendChild( doc.createTextNode( css ) ); 286 | } 287 | 288 | //push to appendedEls to track for later removal 289 | appendedEls.push( ss ); 290 | } 291 | }, 292 | //tweaked Ajax functions from Quirksmode 293 | ajax = function( url, callback ) { 294 | var req = xmlHttp(); 295 | if (!req){ 296 | return; 297 | } 298 | req.open( "GET", url, true ); 299 | req.onreadystatechange = function () { 300 | if ( req.readyState != 4 || req.status != 200 && req.status != 304 ){ 301 | return; 302 | } 303 | callback( req.responseText ); 304 | } 305 | if ( req.readyState == 4 ){ 306 | return; 307 | } 308 | req.send( null ); 309 | }, 310 | //define ajax obj 311 | xmlHttp = (function() { 312 | var xmlhttpmethod = false; 313 | try { 314 | xmlhttpmethod = new XMLHttpRequest(); 315 | } 316 | catch( e ){ 317 | xmlhttpmethod = new ActiveXObject( "Microsoft.XMLHTTP" ); 318 | } 319 | return function(){ 320 | return xmlhttpmethod; 321 | }; 322 | })(); 323 | 324 | //translate CSS 325 | ripCSS(); 326 | 327 | //expose update for re-running respond later on 328 | respond.update = ripCSS; 329 | 330 | //adjust on resize 331 | function callMedia(){ 332 | applyMedia( true ); 333 | } 334 | if( win.addEventListener ){ 335 | win.addEventListener( "resize", callMedia, false ); 336 | } 337 | else if( win.attachEvent ){ 338 | win.attachEvent( "onresize", callMedia ); 339 | } 340 | })(this); 341 | -------------------------------------------------------------------------------- /WebApp/Scripts/respond.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ 16 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ 17 | window.matchMedia=window.matchMedia||(function(e,f){var c,a=e.documentElement,b=a.firstElementChild||a.firstChild,d=e.createElement("body"),g=e.createElement("div");g.id="mq-test-1";g.style.cssText="position:absolute;top:-100em";d.style.background="none";d.appendChild(g);return function(h){g.innerHTML='­';a.insertBefore(d,b);c=g.offsetWidth==42;a.removeChild(d);return{matches:c,media:h}}})(document); 18 | 19 | /*! Respond.js v1.2.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 20 | (function(e){e.respond={};respond.update=function(){};respond.mediaQueriesSupported=e.matchMedia&&e.matchMedia("only all").matches;if(respond.mediaQueriesSupported){return}var w=e.document,s=w.documentElement,i=[],k=[],q=[],o={},h=30,f=w.getElementsByTagName("head")[0]||s,g=w.getElementsByTagName("base")[0],b=f.getElementsByTagName("link"),d=[],a=function(){var D=b,y=D.length,B=0,A,z,C,x;for(;B-1,minw:F.match(/\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:F.match(/\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}}j()},l,r,v=function(){var z,A=w.createElement("div"),x=w.body,y=false;A.style.cssText="position:absolute;font-size:1em;width:1em";if(!x){x=y=w.createElement("body");x.style.background="none"}x.appendChild(A);s.insertBefore(x,s.firstChild);z=A.offsetWidth;if(y){s.removeChild(x)}else{x.removeChild(A)}z=p=parseFloat(z);return z},p,j=function(I){var x="clientWidth",B=s[x],H=w.compatMode==="CSS1Compat"&&B||w.body[x]||B,D={},G=b[b.length-1],z=(new Date()).getTime();if(I&&l&&z-l-1?(p||v()):1)}if(!!J){J=parseFloat(J)*(J.indexOf(y)>-1?(p||v()):1)}if(!K.hasquery||(!A||!L)&&(A||H>=C)&&(L||H<=J)){if(!D[K.media]){D[K.media]=[]}D[K.media].push(k[K.rules])}}for(var E in q){if(q[E]&&q[E].parentNode===f){f.removeChild(q[E])}}for(var E in D){var M=w.createElement("style"),F=D[E].join("\n");M.type="text/css";M.media=E;f.insertBefore(M,G.nextSibling);if(M.styleSheet){M.styleSheet.cssText=F}else{M.appendChild(w.createTextNode(F))}q.push(M)}},n=function(x,z){var y=c();if(!y){return}y.open("GET",x,true);y.onreadystatechange=function(){if(y.readyState!=4||y.status!=200&&y.status!=304){return}z(y.responseText)};if(y.readyState==4){return}y.send(null)},c=(function(){var x=false;try{x=new XMLHttpRequest()}catch(y){x=new ActiveXObject("Microsoft.XMLHTTP")}return function(){return x}})();a();respond.update=a;function t(){j(true)}if(e.addEventListener){e.addEventListener("resize",t,false)}else{if(e.attachEvent){e.attachEvent("onresize",t)}}})(this); -------------------------------------------------------------------------------- /WebApp/Views/Home/About.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "About"; 3 | } 4 |

    @ViewBag.Title.

    5 |

    @ViewBag.Message

    6 | 7 |

    Use this area to provide additional information.

    8 | -------------------------------------------------------------------------------- /WebApp/Views/Home/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Contact"; 3 | } 4 |

    @ViewBag.Title.

    5 |

    @ViewBag.Message

    6 | 7 |
    8 | One Microsoft Way
    9 | Redmond, WA 98052-6399
    10 | P: 11 | 425.555.0100 12 |
    13 | 14 |
    15 | Support: Support@example.com
    16 | Marketing: Marketing@example.com 17 |
    -------------------------------------------------------------------------------- /WebApp/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Home Page"; 3 | } 4 | 5 |
    6 |

    ASP.NET

    7 |

    ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS and JavaScript.

    8 |

    Learn more »

    9 |
    10 | 11 |
    12 |
    13 |

    Getting started

    14 |

    15 | ASP.NET MVC gives you a powerful, patterns-based way to build dynamic websites that 16 | enables a clean separation of concerns and gives you full control over markup 17 | for enjoyable, agile development. 18 |

    19 |

    Learn more »

    20 |
    21 |
    22 |

    Get more libraries

    23 |

    NuGet is a free Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects.

    24 |

    Learn more »

    25 |
    26 |
    27 |

    Web Hosting

    28 |

    You can easily find a web hosting company that offers the right mix of features and price for your applications.

    29 |

    Learn more »

    30 |
    31 |
    -------------------------------------------------------------------------------- /WebApp/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Error 6 | 7 | 8 |
    9 |

    Error.

    10 |

    An error occurred while processing your request.

    11 |
    12 | 13 | 14 | -------------------------------------------------------------------------------- /WebApp/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewBag.Title - My ASP.NET Application 7 | @Styles.Render("~/Content/css") 8 | @Scripts.Render("~/bundles/modernizr") 9 | 10 | 11 | 30 |
    31 | @RenderBody() 32 |
    33 |
    34 |

    © @DateTime.Now.Year - My ASP.NET Application

    35 |
    36 |
    37 | 38 | @Scripts.Render("~/bundles/jquery") 39 | @Scripts.Render("~/bundles/bootstrap") 40 | @RenderSection("scripts", required: false) 41 | 42 | 43 | -------------------------------------------------------------------------------- /WebApp/Views/Tenant/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model List 2 | 3 | @{ 4 | Layout = null; 5 | } 6 | 7 | 8 | 9 | 10 | 11 | List of tenants 12 | 13 | 14 |
    15 |
      16 | @foreach (var tenant in @Model) 17 | { 18 |
    • @tenant.Name
    • 19 |
    • ...@tenant.DomainName
    • 20 | } 21 | 22 | 23 |
    24 | 25 |
    26 | 27 | -------------------------------------------------------------------------------- /WebApp/Views/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 |
    7 |
    8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /WebApp/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /WebApp/Web.Debug.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /WebApp/Web.Release.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /WebApp/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 |
    10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /WebApp/WebApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Debug 8 | AnyCPU 9 | 10 | 11 | 2.0 12 | {34C50F90-32C4-4180-AA7D-97876C447137} 13 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 14 | Library 15 | Properties 16 | WebApp 17 | WebApp 18 | v4.5.2 19 | false 20 | true 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | true 31 | full 32 | false 33 | bin\ 34 | DEBUG;TRACE 35 | prompt 36 | 4 37 | 38 | 39 | pdbonly 40 | true 41 | bin\ 42 | TRACE 43 | prompt 44 | 4 45 | 46 | 47 | 48 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll 49 | True 50 | 51 | 52 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll 53 | True 54 | 55 | 56 | ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll 57 | True 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | True 80 | ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll 81 | 82 | 83 | 84 | 85 | 86 | 87 | True 88 | ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll 89 | 90 | 91 | True 92 | ..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll 93 | 94 | 95 | ..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll 96 | 97 | 98 | True 99 | ..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll 100 | 101 | 102 | True 103 | ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.dll 104 | 105 | 106 | True 107 | ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Deployment.dll 108 | 109 | 110 | True 111 | ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll 112 | 113 | 114 | True 115 | ..\packages\WebGrease.1.5.2\lib\WebGrease.dll 116 | 117 | 118 | True 119 | ..\packages\Antlr.3.4.1.9004\lib\Antlr3.Runtime.dll 120 | 121 | 122 | 123 | 124 | ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll 125 | 126 | 127 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 128 | 129 | 130 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 131 | 132 | 133 | ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | Global.asax 147 | 148 | 149 | 150 | 201512140630036_InitialCreate.cs 151 | 152 | 153 | 154 | 201512140634446_sessions-and-speakers.cs 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | Web.config 187 | 188 | 189 | Web.config 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 201512140630036_InitialCreate.cs 214 | 215 | 216 | 201512140634446_sessions-and-speakers.cs 217 | 218 | 219 | 220 | 10.0 221 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | True 234 | True 235 | 1318 236 | / 237 | http://localhost:1318/ 238 | False 239 | False 240 | 241 | 242 | False 243 | 244 | 245 | 246 | 247 | 248 | 249 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 250 | 251 | 252 | 253 | 254 | 260 | -------------------------------------------------------------------------------- /WebApp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellner/pluralsight-multitenantaspnet/8bf05004d84d19ff95a75f0bfd130f76c76fbac0/WebApp/favicon.ico -------------------------------------------------------------------------------- /WebApp/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellner/pluralsight-multitenantaspnet/8bf05004d84d19ff95a75f0bfd130f76c76fbac0/WebApp/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /WebApp/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellner/pluralsight-multitenantaspnet/8bf05004d84d19ff95a75f0bfd130f76c76fbac0/WebApp/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /WebApp/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellner/pluralsight-multitenantaspnet/8bf05004d84d19ff95a75f0bfd130f76c76fbac0/WebApp/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /WebApp/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # ASP.NET in Multi-tenant App, Examples in MVC, ExtJS, and Angular 4 | 5 | This is the starting code for the Pluralsight Course. If you would like the full source, head over to the Pluralsight web site and make sure you have licensed the content such that you have access to the full source. 6 | 7 | https://app.pluralsight.com/library/courses/aspnet-multi-tenant-app-mvc-extjs-angular/table-of-contents 8 | 9 | ## Course Description 10 | 11 | I will teach you, an ASP.NET web site developer, how to build a first class best practices web site that supports multiple domains (also known as a multiple tenants) from both a server side and client side perspective. I will show you how to segregate both the data and theme of each unique domain (tenant) such that each tenant (unique domain) has its own private web site logic and theme. I will show you how to capture the incoming request on the server side and parse that request for the domain, and then based on that parse I will show you how to guide your code through different programming logic and display themes. After I've shown you how to build a best practices server side multi-tenant web site, I will teach you how to build separately a 100 percent client side apps that leverage our web server code using the client side SPA (Single Page JavaScript App) libraries Angular and ExtJS. As part of this course, I will teach you how to setup and use Node.js and Gulp as your build system for Angular apps. I will also teach you how to use Sencha's CMD for scaffolding your ExtJS app as well as building and minifying the ExtJS app for production. Finally I will teach you the details around deploying your application (including dns considerations) to an on premise service as well as Amazon's AWS and Microsoft Azure Clouds. You will also learn advanced data caching patterns inclding taking advantage of Redis for distributed caching. 12 | 13 | 14 | ##Special Note: 15 | 16 | I've found that EF is a little picky about how it wants to initialize 17 | the Sql Server Express database which is in the directory /AppData. 18 | 19 | First of all check if the folder named App_Data exists in your cloned folder. If it doesn't, try 20 | manually creating this folder and launching the solution again. 21 | 22 | It's important that you don't simply delete the database files but that you do that 23 | through the menu choice on VS View/Sql Server objects. Drill into databases 24 | and then right click and remove the dbmultitenant database and make sure to click 25 | checkbox "close connections". 26 | 27 | This will delete those two files in the AppData directory and new files will be created 28 | when the project is run. If you have issues, I suggest making sure to stop the asp.net 29 | web server (IIS Express) and let VS restart it. That might help. 30 | 31 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | # ASP.NET in Multi-tenant App, Examples in MVC, ExtJS, and Angular 4 | 5 | This is the starting code for the Pluralsight Course. If you would like the full source, head over to the Pluralsight web site and make sure you have licensed the content such that you have access to the full source. 6 | 7 | https://app.pluralsight.com/library/courses/aspnet-multi-tenant-app-mvc-extjs-angular/table-of-contents 8 | 9 | ## Course Description 10 | 11 | I will teach you, an ASP.NET web site developer, how to build a first class best practices web site that supports multiple domains (also known as a multiple tenants) from both a server side and client side perspective. I will show you how to segregate both the data and theme of each unique domain (tenant) such that each tenant (unique domain) has its own private web site logic and theme. I will show you how to capture the incoming request on the server side and parse that request for the domain, and then based on that parse I will show you how to guide your code through different programming logic and display themes. After I've shown you how to build a best practices server side multi-tenant web site, I will teach you how to build separately a 100 percent client side apps that leverage our web server code using the client side SPA (Single Page JavaScript App) libraries Angular and ExtJS. As part of this course, I will teach you how to setup and use Node.js and Gulp as your build system for Angular apps. I will also teach you how to use Sencha's CMD for scaffolding your ExtJS app as well as building and minifying the ExtJS app for production. Finally I will teach you the details around deploying your application (including dns considerations) to an on premise service as well as Amazon's AWS and Microsoft Azure Clouds. You will also learn advanced data caching patterns inclding taking advantage of Redis for distributed caching. 12 | 13 | 14 | ##Special Note: 15 | 16 | I've found that EF is a little picky about how it wants to initialize 17 | the Sql Server Express database which is in the directory /AppData. 18 | 19 | It's important that you don't simply delete the database files but that you do that 20 | through the menu choice on VS View/Sql Server objects. Drill into databases 21 | and then right click and remove the dbmultitenant database and make sure to click 22 | checkbox "close connections". 23 | 24 | This will delete those two files in the AppData directory and new files will be created 25 | when the project is run. If you have issues, I suggest making sure to stop the asp.net 26 | web server (IIS Express) and let VS restart it. That might help. 27 | 28 | --------------------------------------------------------------------------------