From 5f97bc8ae389a5e646f9d74d74c1188ffef5ca1d Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Mon, 26 Nov 2012 06:05:30 -0700
Subject: [PATCH 01/97] #153 - fixed bad parsing of regex route constraint
patterns that use a comma.
---
.../AttributeRouting.Specs.csproj | 5 +-
.../Features/InheritingActions.feature.cs | 54 ++--
.../Features/Logging.feature.cs | 12 +-
.../Features/RouteAreas.feature.cs | 90 +++---
.../Features/RouteConstraints.feature | 1 +
.../Features/RouteConstraints.feature.cs | 261 +++++++++---------
.../Features/RouteConventions.feature.cs | 144 +++++-----
.../Features/RouteDefaults.feature.cs | 42 +--
.../Features/RoutePrecedence.feature.cs | 228 +++++++--------
.../Features/RoutePrefixes.feature.cs | 102 +++----
.../Features/StandardUsage.feature.cs | 76 ++---
.../HttpInlineRouteConstraintsControllers.cs | 6 +
.../InlineRouteConstraintsControllers.cs | 6 +
src/AttributeRouting.Specs/app.config | 2 +-
src/AttributeRouting.Specs/packages.config | 2 +-
.../Framework/RouteBuilder.cs | 9 +-
16 files changed, 530 insertions(+), 510 deletions(-)
diff --git a/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj b/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
index 2c0000a..49f1048 100644
--- a/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
+++ b/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
@@ -81,9 +81,8 @@
-
- False
- ..\packages\SpecFlow.1.8.1\lib\net35\TechTalk.SpecFlow.dll
+
+ ..\packages\SpecFlow.1.9.0\lib\net35\TechTalk.SpecFlow.dll
diff --git a/src/AttributeRouting.Specs/Features/InheritingActions.feature.cs b/src/AttributeRouting.Specs/Features/InheritingActions.feature.cs
index 0f21fd5..6b5284b 100644
--- a/src/AttributeRouting.Specs/Features/InheritingActions.feature.cs
+++ b/src/AttributeRouting.Specs/Features/InheritingActions.feature.cs
@@ -1,8 +1,8 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.8.1.0
-// SpecFlow Generator Version:1.8.0.0
+// SpecFlow Version:1.9.1.84
+// SpecFlow Generator Version:1.9.0.0
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.8.1.0")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.1.84")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Inheriting Actions")]
@@ -72,17 +72,17 @@ public virtual void InheritingActionsInADerivedController()
#line 3
this.ScenarioSetup(scenarioInfo);
#line 4
- testRunner.Given("I have registered the routes for the SuperController");
+ testRunner.Given("I have registered the routes for the SuperController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 5
- testRunner.When("I fetch the routes for the SuperController\'s Index action");
+ testRunner.When("I fetch the routes for the SuperController\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 6
- testRunner.Then("the route url is \"InheritedActions/Index\"");
+ testRunner.Then("the route url is \"InheritedActions/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 7
- testRunner.Given("I have registered the routes for the DerivedController");
+ testRunner.Given("I have registered the routes for the DerivedController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 8
- testRunner.When("I fetch the routes for the DerivedController\'s Index action");
+ testRunner.When("I fetch the routes for the DerivedController\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 9
- testRunner.Then("the route url is \"InheritedActions/Index\"");
+ testRunner.Then("the route url is \"InheritedActions/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -95,17 +95,17 @@ public virtual void InheritingActionsInADerivedControllerOverridingTheUrlOfAnAct
#line 11
this.ScenarioSetup(scenarioInfo);
#line 12
- testRunner.Given("I have registered the routes for the SuperController");
+ testRunner.Given("I have registered the routes for the SuperController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 13
- testRunner.When("I fetch the routes for the SuperController\'s Index action");
+ testRunner.When("I fetch the routes for the SuperController\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 14
- testRunner.Then("the route url is \"InheritedActions/Index\"");
+ testRunner.Then("the route url is \"InheritedActions/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 15
- testRunner.Given("I have registered the routes for the DerivedWithOverrideController");
+ testRunner.Given("I have registered the routes for the DerivedWithOverrideController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 16
- testRunner.When("I fetch the routes for the DerivedWithOverrideController\'s Index action");
+ testRunner.When("I fetch the routes for the DerivedWithOverrideController\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 17
- testRunner.Then("the route url is \"InheritedActions/IndexDerived\"");
+ testRunner.Then("the route url is \"InheritedActions/IndexDerived\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -118,17 +118,17 @@ public virtual void InheritingActionsInADerivedControllerThatSpecifiesAnArea()
#line 19
this.ScenarioSetup(scenarioInfo);
#line 20
- testRunner.Given("I have registered the routes for the SuperWithAreaController");
+ testRunner.Given("I have registered the routes for the SuperWithAreaController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 21
- testRunner.When("I fetch the routes for the SuperWithAreaController\'s Index action");
+ testRunner.When("I fetch the routes for the SuperWithAreaController\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 22
- testRunner.Then("the route url is \"Super/InheritedActions/Index\"");
+ testRunner.Then("the route url is \"Super/InheritedActions/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 23
- testRunner.Given("I have registered the routes for the DerivedWithAreaController");
+ testRunner.Given("I have registered the routes for the DerivedWithAreaController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 24
- testRunner.When("I fetch the routes for the DerivedWithAreaController\'s Index action");
+ testRunner.When("I fetch the routes for the DerivedWithAreaController\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 25
- testRunner.Then("the route url is \"Derived/InheritedActions/Index\"");
+ testRunner.Then("the route url is \"Derived/InheritedActions/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -141,17 +141,17 @@ public virtual void InheritingActionsInADerivedControllerThatSpecifiesAPrefix()
#line 27
this.ScenarioSetup(scenarioInfo);
#line 28
- testRunner.Given("I have registered the routes for the SuperWithPrefixController");
+ testRunner.Given("I have registered the routes for the SuperWithPrefixController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 29
- testRunner.When("I fetch the routes for the SuperWithPrefixController\'s Index action");
+ testRunner.When("I fetch the routes for the SuperWithPrefixController\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 30
- testRunner.Then("the route url is \"InheritedActions/Super/Index\"");
+ testRunner.Then("the route url is \"InheritedActions/Super/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 31
- testRunner.Given("I have registered the routes for the DerivedWithPrefixController");
+ testRunner.Given("I have registered the routes for the DerivedWithPrefixController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 32
- testRunner.When("I fetch the routes for the DerivedWithPrefixController\'s Index action");
+ testRunner.When("I fetch the routes for the DerivedWithPrefixController\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 33
- testRunner.Then("the route url is \"InheritedActions/Derived/Index\"");
+ testRunner.Then("the route url is \"InheritedActions/Derived/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Features/Logging.feature.cs b/src/AttributeRouting.Specs/Features/Logging.feature.cs
index 4b3307c..b033593 100644
--- a/src/AttributeRouting.Specs/Features/Logging.feature.cs
+++ b/src/AttributeRouting.Specs/Features/Logging.feature.cs
@@ -1,8 +1,8 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.8.1.0
-// SpecFlow Generator Version:1.8.0.0
+// SpecFlow Version:1.9.1.84
+// SpecFlow Generator Version:1.9.0.0
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.8.1.0")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.1.84")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Logging")]
@@ -72,11 +72,11 @@ public virtual void LogTheRoutesToTheStandardOutput()
#line 3
this.ScenarioSetup(scenarioInfo);
#line 4
- testRunner.Given("I generate the routes defined in the subject controllers");
+ testRunner.Given("I generate the routes defined in the subject controllers", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 5
- testRunner.When("I log the routes");
+ testRunner.When("I log the routes", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 6
- testRunner.Then("ta-da!");
+ testRunner.Then("ta-da!", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs b/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs
index d15ef4c..63879f1 100644
--- a/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs
@@ -1,8 +1,8 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.8.1.0
-// SpecFlow Generator Version:1.8.0.0
+// SpecFlow Version:1.9.1.84
+// SpecFlow Generator Version:1.9.0.0
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.8.1.0")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.1.84")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Route Areas")]
@@ -72,21 +72,21 @@ public virtual void GeneratingAreaRoutes()
#line 3
this.ScenarioSetup(scenarioInfo);
#line 5
- testRunner.Given("I have registered the routes for the AreasController");
+ testRunner.Given("I have registered the routes for the AreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 6
- testRunner.When("I fetch the routes for the Areas controller\'s Index action");
+ testRunner.When("I fetch the routes for the Areas controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 7
- testRunner.Then("the route url is \"Area/Index\"");
+ testRunner.Then("the route url is \"Area/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 8
- testRunner.And("the data token for \"area\" is \"Area\"");
+ testRunner.And("the data token for \"area\" is \"Area\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 10
- testRunner.Given("I have registered the routes for the HttpAreasController");
+ testRunner.Given("I have registered the routes for the HttpAreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 11
- testRunner.When("I fetch the routes for the HttpAreas controller\'s Get action");
+ testRunner.When("I fetch the routes for the HttpAreas controller\'s Get action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 12
- testRunner.Then("the route url is \"ApiArea/Get\"");
+ testRunner.Then("the route url is \"ApiArea/Get\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 13
- testRunner.And("the data token for \"area\" is \"ApiArea\"");
+ testRunner.And("the data token for \"area\" is \"ApiArea\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -99,17 +99,17 @@ public virtual void GeneratingAreaRoutesWhenRouteUrlsSpecifyADuplicateAreaPrefix
#line 15
this.ScenarioSetup(scenarioInfo);
#line 17
- testRunner.Given("I have registered the routes for the AreasController");
+ testRunner.Given("I have registered the routes for the AreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 18
- testRunner.When("I fetch the routes for the Areas controller\'s DuplicatePrefix action");
+ testRunner.When("I fetch the routes for the Areas controller\'s DuplicatePrefix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 19
- testRunner.Then("the route url is \"Area/DuplicatePrefix\"");
+ testRunner.Then("the route url is \"Area/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 21
- testRunner.Given("I have registered the routes for the HttpAreasController");
+ testRunner.Given("I have registered the routes for the HttpAreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 22
- testRunner.When("I fetch the routes for the HttpAreas controller\'s DuplicatePrefix action");
+ testRunner.When("I fetch the routes for the HttpAreas controller\'s DuplicatePrefix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 23
- testRunner.Then("the route url is \"ApiArea/DuplicatePrefix\"");
+ testRunner.Then("the route url is \"ApiArea/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -122,17 +122,17 @@ public virtual void GeneratingAbsoluteRoutesWhenARouteAreaIsDefined()
#line 25
this.ScenarioSetup(scenarioInfo);
#line 27
- testRunner.Given("I have registered the routes for the AreasController");
+ testRunner.Given("I have registered the routes for the AreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 28
- testRunner.When("I fetch the routes for the Areas controller\'s Absolute action");
+ testRunner.When("I fetch the routes for the Areas controller\'s Absolute action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 29
- testRunner.Then("the route url is \"AreaAbsolute\"");
+ testRunner.Then("the route url is \"AreaAbsolute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 31
- testRunner.Given("I have registered the routes for the HttpAreasController");
+ testRunner.Given("I have registered the routes for the HttpAreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 32
- testRunner.When("I fetch the routes for the HttpAreas controller\'s Absolute action");
+ testRunner.When("I fetch the routes for the HttpAreas controller\'s Absolute action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 33
- testRunner.Then("the route url is \"ApiAreaAbsolute\"");
+ testRunner.Then("the route url is \"ApiAreaAbsolute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -145,17 +145,17 @@ public virtual void GeneratingAreaRoutesWhenRouteUrlStartsWithTheAreaPrefix()
#line 35
this.ScenarioSetup(scenarioInfo);
#line 37
- testRunner.Given("I have registered the routes for the AreasController");
+ testRunner.Given("I have registered the routes for the AreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 38
- testRunner.When("I fetch the routes for the Areas controller\'s RouteBeginsWithAreaName action");
+ testRunner.When("I fetch the routes for the Areas controller\'s RouteBeginsWithAreaName action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 39
- testRunner.Then("the route url is \"Area/Areas\"");
+ testRunner.Then("the route url is \"Area/Areas\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 41
- testRunner.Given("I have registered the routes for the HttpAreasController");
+ testRunner.Given("I have registered the routes for the HttpAreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 42
- testRunner.When("I fetch the routes for the HttpAreas controller\'s RouteBeginsWithAreaName action");
+ testRunner.When("I fetch the routes for the HttpAreas controller\'s RouteBeginsWithAreaName action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 43
- testRunner.Then("the route url is \"ApiArea/ApiAreas\"");
+ testRunner.Then("the route url is \"ApiArea/ApiAreas\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -168,21 +168,21 @@ public virtual void GeneratingAreaRoutesWithAnExplicitAreaUrl()
#line 45
this.ScenarioSetup(scenarioInfo);
#line 47
- testRunner.Given("I have registered the routes for the ExplicitAreaUrlController");
+ testRunner.Given("I have registered the routes for the ExplicitAreaUrlController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 48
- testRunner.When("I fetch the routes for the ExplicitAreaUrl controller\'s Index action");
+ testRunner.When("I fetch the routes for the ExplicitAreaUrl controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 49
- testRunner.Then("the route url is \"ExplicitArea/Index\"");
+ testRunner.Then("the route url is \"ExplicitArea/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 50
- testRunner.And("the data token for \"area\" is \"Area\"");
+ testRunner.And("the data token for \"area\" is \"Area\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 52
- testRunner.Given("I have registered the routes for the HttpExplicitAreaUrlController");
+ testRunner.Given("I have registered the routes for the HttpExplicitAreaUrlController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 53
- testRunner.When("I fetch the routes for the HttpExplicitAreaUrl controller\'s Get action");
+ testRunner.When("I fetch the routes for the HttpExplicitAreaUrl controller\'s Get action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 54
- testRunner.Then("the route url is \"ApiExplicitArea/Get\"");
+ testRunner.Then("the route url is \"ApiExplicitArea/Get\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 55
- testRunner.And("the data token for \"area\" is \"ApiArea\"");
+ testRunner.And("the data token for \"area\" is \"ApiArea\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -197,22 +197,22 @@ public virtual void GeneratingAreaRoutesWithAnExplicitAreaUrlWhenRouteUrlsSpecif
#line 57
this.ScenarioSetup(scenarioInfo);
#line 59
- testRunner.Given("I have registered the routes for the ExplicitAreaUrlController");
+ testRunner.Given("I have registered the routes for the ExplicitAreaUrlController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 60
- testRunner.When("I fetch the routes for the ExplicitAreaUrl controller\'s DuplicatePrefix action");
+ testRunner.When("I fetch the routes for the ExplicitAreaUrl controller\'s DuplicatePrefix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 61
- testRunner.Then("the route url is \"ExplicitArea/DuplicatePrefix\"");
+ testRunner.Then("the route url is \"ExplicitArea/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 62
- testRunner.And("the data token for \"area\" is \"Area\"");
+ testRunner.And("the data token for \"area\" is \"Area\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 64
- testRunner.Given("I have registered the routes for the HttpExplicitAreaUrlController");
+ testRunner.Given("I have registered the routes for the HttpExplicitAreaUrlController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 65
testRunner.When("I fetch the routes for the HttpExplicitAreaUrl controller\'s DuplicatePrefix actio" +
- "n");
+ "n", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 66
- testRunner.Then("the route url is \"ApiExplicitArea/DuplicatePrefix\"");
+ testRunner.Then("the route url is \"ApiExplicitArea/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 67
- testRunner.And("the data token for \"area\" is \"ApiArea\"");
+ testRunner.And("the data token for \"area\" is \"ApiArea\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Features/RouteConstraints.feature b/src/AttributeRouting.Specs/Features/RouteConstraints.feature
index ecc4d16..c8aea4e 100644
--- a/src/AttributeRouting.Specs/Features/RouteConstraints.feature
+++ b/src/AttributeRouting.Specs/Features/RouteConstraints.feature
@@ -30,6 +30,7 @@ Scenario Outline: Inline constraints
| Max | MaxRouteConstraint |
| Range | RangeRouteConstraint |
| Regex | RegexRouteConstraint |
+ | RegexRange | RegexRouteConstraint |
| Compound | IntRouteConstraint |
| Compound | MaxRouteConstraint |
diff --git a/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs b/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
index 3ade24f..e13d6d2 100644
--- a/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
@@ -1,8 +1,8 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.8.1.0
-// SpecFlow Generator Version:1.8.0.0
+// SpecFlow Version:1.9.0.77
+// SpecFlow Generator Version:1.9.0.0
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.8.1.0")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Route Constraints")]
@@ -66,48 +66,49 @@ public virtual void ScenarioCleanup()
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Inline constraints")]
- [NUnit.Framework.TestCaseAttribute("Alpha", "AlphaRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Int", "IntRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Long", "LongRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Float", "FloatRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Double", "DoubleRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Decimal", "DecimalRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Bool", "BoolRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Guid", "GuidRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("DateTime", "DateTimeRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Length", "LengthRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("MinLength", "MinLengthRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("MaxLength", "MaxLengthRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("LengthRange", "LengthRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Min", "MinRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Max", "MaxRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Range", "RangeRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Regex", "RegexRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Compound", "IntRouteConstraint", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Compound", "MaxRouteConstraint", new string[0])]
+ [NUnit.Framework.TestCaseAttribute("Alpha", "AlphaRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Int", "IntRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Long", "LongRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Float", "FloatRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Double", "DoubleRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Decimal", "DecimalRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Bool", "BoolRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Guid", "GuidRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("DateTime", "DateTimeRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Length", "LengthRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("MinLength", "MinLengthRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("MaxLength", "MaxLengthRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("LengthRange", "LengthRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Min", "MinRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Max", "MaxRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Range", "RangeRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Regex", "RegexRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("RegexRange", "RegexRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Compound", "IntRouteConstraint", null)]
+ [NUnit.Framework.TestCaseAttribute("Compound", "MaxRouteConstraint", null)]
public virtual void InlineConstraints(string actionName, string constraintTypeName, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Inline constraints", exampleTags);
#line 3
this.ScenarioSetup(scenarioInfo);
#line 5
- testRunner.Given("I have registered the routes for the InlineRouteConstraintsController");
+ testRunner.Given("I have registered the routes for the InlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 6
- testRunner.When(string.Format("I fetch the routes for the InlineRouteConstraints controller\'s {0} action", actionName));
+ testRunner.When(string.Format("I fetch the routes for the InlineRouteConstraints controller\'s {0} action", actionName), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 7
- testRunner.Then(string.Format("the route url is \"Inline-Constraints/{0}/{{x}}\"", actionName));
+ testRunner.Then(string.Format("the route url is \"Inline-Constraints/{0}/{{x}}\"", actionName), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 8
testRunner.And(string.Format("the parameter \"x\" is constrained by an inline AttributeRouting.Web.Constraints.{0" +
- "}", constraintTypeName));
+ "}", constraintTypeName), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 10
- testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController");
+ testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 11
- testRunner.When(string.Format("I fetch the routes for the HttpInlineRouteConstraints controller\'s {0} action", actionName));
+ testRunner.When(string.Format("I fetch the routes for the HttpInlineRouteConstraints controller\'s {0} action", actionName), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 12
- testRunner.Then(string.Format("the route url is \"Http-Inline-Constraints/{0}/{{x}}\"", actionName));
+ testRunner.Then(string.Format("the route url is \"Http-Inline-Constraints/{0}/{{x}}\"", actionName), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 13
testRunner.And(string.Format("the parameter \"x\" is constrained by an inline AttributeRouting.Web.Constraints.{0" +
- "}", constraintTypeName));
+ "}", constraintTypeName), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -117,34 +118,34 @@ public virtual void InlineConstraints(string actionName, string constraintTypeNa
public virtual void MultipleInlineConstraintsPerUrlSegment()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Multiple inline constraints per url segment", ((string[])(null)));
-#line 36
+#line 37
this.ScenarioSetup(scenarioInfo);
-#line 38
- testRunner.Given("I have registered the routes for the InlineRouteConstraintsController");
#line 39
- testRunner.When("I fetch the routes for the InlineRouteConstraints controller\'s MultipleWithinUrlS" +
- "egment action");
+ testRunner.Given("I have registered the routes for the InlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 40
- testRunner.Then("the route url is \"Inline-Constraints/avatar/{width}x{height}/{image}\"");
+ testRunner.When("I fetch the routes for the InlineRouteConstraints controller\'s MultipleWithinUrlS" +
+ "egment action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 41
- testRunner.And("the parameter \"width\" is constrained by an inline AttributeRouting.Web.Constraint" +
- "s.IntRouteConstraint");
+ testRunner.Then("the route url is \"Inline-Constraints/avatar/{width}x{height}/{image}\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 42
+ testRunner.And("the parameter \"width\" is constrained by an inline AttributeRouting.Web.Constraint" +
+ "s.IntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 43
testRunner.And("the parameter \"height\" is constrained by an inline AttributeRouting.Web.Constrain" +
- "ts.IntRouteConstraint");
-#line 44
- testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController");
+ "ts.IntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 45
- testRunner.When("I fetch the routes for the HttpInlineRouteConstraints controller\'s MultipleWithin" +
- "UrlSegment action");
+ testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 46
- testRunner.Then("the route url is \"Http-Inline-Constraints/avatar/{width}x{height}/{image}\"");
+ testRunner.When("I fetch the routes for the HttpInlineRouteConstraints controller\'s MultipleWithin" +
+ "UrlSegment action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 47
- testRunner.And("the parameter \"width\" is constrained by an inline AttributeRouting.Web.Constraint" +
- "s.IntRouteConstraint");
+ testRunner.Then("the route url is \"Http-Inline-Constraints/avatar/{width}x{height}/{image}\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 48
+ testRunner.And("the parameter \"width\" is constrained by an inline AttributeRouting.Web.Constraint" +
+ "s.IntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 49
testRunner.And("the parameter \"height\" is constrained by an inline AttributeRouting.Web.Constrain" +
- "ts.IntRouteConstraint");
+ "ts.IntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -154,29 +155,29 @@ public virtual void MultipleInlineConstraintsPerUrlSegment()
public virtual void InlineConstraintsSpecifiedInTheRoutePrefixAttribute()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Inline constraints specified in the RoutePrefixAttribute", ((string[])(null)));
-#line 50
+#line 51
this.ScenarioSetup(scenarioInfo);
-#line 52
- testRunner.Given("I have registered the routes for the PrefixedInlineRouteConstraintsController");
#line 53
- testRunner.When("I fetch the routes for the PrefixedInlineRouteConstraints controller\'s Index acti" +
- "on");
+ testRunner.Given("I have registered the routes for the PrefixedInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 54
- testRunner.Then("the route url is \"Prefixed-Inline-Constraints/{id}/Howdy\"");
+ testRunner.When("I fetch the routes for the PrefixedInlineRouteConstraints controller\'s Index acti" +
+ "on", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 55
+ testRunner.Then("the route url is \"Prefixed-Inline-Constraints/{id}/Howdy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 56
testRunner.And("the parameter \"id\" is constrained by an inline AttributeRouting.Web.Constraints.I" +
- "ntRouteConstraint");
-#line 57
- testRunner.Given("I have registered the routes for the HttpPrefixedInlineRouteConstraintsController" +
- "");
+ "ntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 58
- testRunner.When("I fetch the routes for the HttpPrefixedInlineRouteConstraints controller\'s Index " +
- "action");
+ testRunner.Given("I have registered the routes for the HttpPrefixedInlineRouteConstraintsController" +
+ "", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 59
- testRunner.Then("the route url is \"Http-Prefixed-Inline-Constraints/{id}/Howdy\"");
+ testRunner.When("I fetch the routes for the HttpPrefixedInlineRouteConstraints controller\'s Index " +
+ "action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 60
+ testRunner.Then("the route url is \"Http-Prefixed-Inline-Constraints/{id}/Howdy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 61
testRunner.And("the parameter \"id\" is constrained by an inline AttributeRouting.Web.Constraints.I" +
- "ntRouteConstraint");
+ "ntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -186,98 +187,98 @@ public virtual void InlineConstraintsSpecifiedInTheRoutePrefixAttribute()
public virtual void InlineConstraintsSpecifiedInTheRouteAreaAttribute()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Inline constraints specified in the RouteAreaAttribute", ((string[])(null)));
-#line 62
+#line 63
this.ScenarioSetup(scenarioInfo);
-#line 64
- testRunner.Given("I have registered the routes for the AreaInlineRouteConstraintsController");
#line 65
- testRunner.When("I fetch the routes for the AreaInlineRouteConstraints controller\'s Index action");
+ testRunner.Given("I have registered the routes for the AreaInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 66
- testRunner.Then("the route url is \"Area-Inline-Constraints/{id}/Howdy\"");
+ testRunner.When("I fetch the routes for the AreaInlineRouteConstraints controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 67
+ testRunner.Then("the route url is \"Area-Inline-Constraints/{id}/Howdy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 68
testRunner.And("the parameter \"id\" is constrained by an inline AttributeRouting.Web.Constraints.I" +
- "ntRouteConstraint");
-#line 69
- testRunner.Given("I have registered the routes for the HttpAreaInlineRouteConstraintsController");
+ "ntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 70
- testRunner.When("I fetch the routes for the HttpAreaInlineRouteConstraints controller\'s Index acti" +
- "on");
+ testRunner.Given("I have registered the routes for the HttpAreaInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 71
- testRunner.Then("the route url is \"Http-Area-Inline-Constraints/{id}/Howdy\"");
+ testRunner.When("I fetch the routes for the HttpAreaInlineRouteConstraints controller\'s Index acti" +
+ "on", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 72
+ testRunner.Then("the route url is \"Http-Area-Inline-Constraints/{id}/Howdy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 73
testRunner.And("the parameter \"id\" is constrained by an inline AttributeRouting.Web.Constraints.I" +
- "ntRouteConstraint");
+ "ntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Matching inline route constraints")]
- [NUnit.Framework.TestCaseAttribute("Alpha/abc", "Alpha", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Alpha/123", "Alpha", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Int/53", "Int", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Int/abc", "Int", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("IntOptional", "IntOptional", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Long/79", "Long", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Long/xyz", "Long", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Float/1.334", "Float", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Float/gg2", "Float", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Double/3.14", "Double", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Double/adf78", "Double", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Decimal/99.32123345", "Decimal", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Decimal/d8uasdf", "Decimal", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Bool/true", "Bool", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Bool/false", "Bool", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Bool/truish", "Bool", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Bool/falsish", "Bool", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Guid/6076668C-57AA-47FD-A914-94FD552C8493", "Guid", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Guid/6076668C-57AA-47FD-A914-94FD552C", "Guid", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("DateTime/2012-4-22", "DateTime", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("DateTime/Today", "DateTime", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Length/a", "Length", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Length/aa", "Length", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("MinLength/abcdefghi", "MinLength", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("MinLength/abcdefghij", "MinLength", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("MaxLength/abcdefghij", "MaxLength", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("MaxLength/abcdefghijk", "MaxLength", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("LengthRange/abcdefghijk", "LengthRange", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("LengthRange/a", "LengthRange", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("LengthRange/ab", "LengthRange", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("LengthRange/abcdefghij", "LengthRange", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("LengthRange/abcdefghijk", "LengthRange", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Min/0", "Min", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Min/1", "Min", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Max/10", "Max", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Max/11", "Max", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Range/0", "Range", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Range/1", "Range", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Range/10", "Range", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Range/11", "Range", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Regex/Howdy", "Regex", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Regex/BoyHowdy", "Regex", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Compound/5", "Compound", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Compound/5.0", "Compound", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Enum/red", "Enum", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Enum/taupe", "Enum", "is not", new string[0])]
- [NUnit.Framework.TestCaseAttribute("WithOptional", "WithOptional", "is", new string[0])]
- [NUnit.Framework.TestCaseAttribute("WithDefault", "WithDefault", "is", new string[0])]
+ [NUnit.Framework.TestCaseAttribute("Alpha/abc", "Alpha", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Alpha/123", "Alpha", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Int/53", "Int", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Int/abc", "Int", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("IntOptional", "IntOptional", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Long/79", "Long", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Long/xyz", "Long", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Float/1.334", "Float", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Float/gg2", "Float", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Double/3.14", "Double", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Double/adf78", "Double", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Decimal/99.32123345", "Decimal", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Decimal/d8uasdf", "Decimal", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Bool/true", "Bool", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Bool/false", "Bool", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Bool/truish", "Bool", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Bool/falsish", "Bool", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Guid/6076668C-57AA-47FD-A914-94FD552C8493", "Guid", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Guid/6076668C-57AA-47FD-A914-94FD552C", "Guid", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("DateTime/2012-4-22", "DateTime", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("DateTime/Today", "DateTime", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Length/a", "Length", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Length/aa", "Length", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("MinLength/abcdefghi", "MinLength", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("MinLength/abcdefghij", "MinLength", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("MaxLength/abcdefghij", "MaxLength", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("MaxLength/abcdefghijk", "MaxLength", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("LengthRange/abcdefghijk", "LengthRange", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("LengthRange/a", "LengthRange", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("LengthRange/ab", "LengthRange", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("LengthRange/abcdefghij", "LengthRange", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("LengthRange/abcdefghijk", "LengthRange", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Min/0", "Min", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Min/1", "Min", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Max/10", "Max", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Max/11", "Max", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Range/0", "Range", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Range/1", "Range", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Range/10", "Range", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Range/11", "Range", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Regex/Howdy", "Regex", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Regex/BoyHowdy", "Regex", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Compound/5", "Compound", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Compound/5.0", "Compound", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Enum/red", "Enum", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Enum/taupe", "Enum", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("WithOptional", "WithOptional", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("WithDefault", "WithDefault", "is", null)]
public virtual void MatchingInlineRouteConstraints(string url, string action, string condition, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Matching inline route constraints", exampleTags);
-#line 74
+#line 75
this.ScenarioSetup(scenarioInfo);
-#line 76
- testRunner.Given("I have registered the routes for the InlineRouteConstraintsController");
#line 77
- testRunner.When(string.Format("a request for \"Inline-Constraints/{0}\" is made", url));
+ testRunner.Given("I have registered the routes for the InlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 78
- testRunner.Then(string.Format("the {0} action {1} matched", action, condition));
-#line 80
- testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController");
+ testRunner.When(string.Format("a request for \"Inline-Constraints/{0}\" is made", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 79
+ testRunner.Then(string.Format("the {0} action {1} matched", action, condition), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 81
- testRunner.When(string.Format("a request for \"Http-Inline-Constraints/{0}\" is made", url));
+ testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 82
- testRunner.Then(string.Format("the {0} action {1} matched", action, condition));
+ testRunner.When(string.Format("a request for \"Http-Inline-Constraints/{0}\" is made", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 83
+ testRunner.Then(string.Format("the {0} action {1} matched", action, condition), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Features/RouteConventions.feature.cs b/src/AttributeRouting.Specs/Features/RouteConventions.feature.cs
index b2dbbc0..f388969 100644
--- a/src/AttributeRouting.Specs/Features/RouteConventions.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteConventions.feature.cs
@@ -1,8 +1,8 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.8.1.0
-// SpecFlow Generator Version:1.8.0.0
+// SpecFlow Version:1.9.1.84
+// SpecFlow Generator Version:1.9.0.0
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.8.1.0")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.1.84")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Route Conventions")]
@@ -66,32 +66,32 @@ public virtual void ScenarioCleanup()
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Generating routes using the RestfulRouteConvention")]
- [NUnit.Framework.TestCaseAttribute("Index", "GET", "RestfulRouteConvention", new string[0])]
- [NUnit.Framework.TestCaseAttribute("New", "GET", "RestfulRouteConvention/New", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Create", "POST", "RestfulRouteConvention", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Show", "GET", "RestfulRouteConvention/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Edit", "GET", "RestfulRouteConvention/{id}/Edit", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Update", "PUT", "RestfulRouteConvention/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Delete", "GET", "RestfulRouteConvention/{id}/Delete", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Destroy", "DELETE", "RestfulRouteConvention/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Custom", "GET", "RestfulRouteConvention/Custom", new string[0])]
+ [NUnit.Framework.TestCaseAttribute("Index", "GET", "RestfulRouteConvention", null)]
+ [NUnit.Framework.TestCaseAttribute("New", "GET", "RestfulRouteConvention/New", null)]
+ [NUnit.Framework.TestCaseAttribute("Create", "POST", "RestfulRouteConvention", null)]
+ [NUnit.Framework.TestCaseAttribute("Show", "GET", "RestfulRouteConvention/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("Edit", "GET", "RestfulRouteConvention/{id}/Edit", null)]
+ [NUnit.Framework.TestCaseAttribute("Update", "PUT", "RestfulRouteConvention/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("Delete", "GET", "RestfulRouteConvention/{id}/Delete", null)]
+ [NUnit.Framework.TestCaseAttribute("Destroy", "DELETE", "RestfulRouteConvention/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("Custom", "GET", "RestfulRouteConvention/Custom", null)]
public virtual void GeneratingRoutesUsingTheRestfulRouteConvention(string action, string method, string url, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes using the RestfulRouteConvention", exampleTags);
#line 3
this.ScenarioSetup(scenarioInfo);
#line 4
- testRunner.Given("I have registered the routes for the RestfulRouteConventionController");
+ testRunner.Given("I have registered the routes for the RestfulRouteConventionController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 5
- testRunner.When(string.Format("I fetch the routes for the RestfulRouteConvention controller\'s {0} action", action));
+ testRunner.When(string.Format("I fetch the routes for the RestfulRouteConvention controller\'s {0} action", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 6
- testRunner.Then(string.Format("the route url is \"{0}\"", url));
+ testRunner.Then(string.Format("the route url is \"{0}\"", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 7
- testRunner.And("the default for \"controller\" is \"RestfulRouteConvention\"");
+ testRunner.And("the default for \"controller\" is \"RestfulRouteConvention\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 8
- testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action));
+ testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 9
- testRunner.And(string.Format("the route for {0} is constrained to {1} requests", action, method));
+ testRunner.And(string.Format("the route for {0} is constrained to {1} requests", action, method), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -99,14 +99,14 @@ public virtual void GeneratingRoutesUsingTheRestfulRouteConvention(string action
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Generating routes using the RestfulRouteConvention on controllers with a RoutePre" +
"fix attribute")]
- [NUnit.Framework.TestCaseAttribute("Index", "GET", "Prefix", new string[0])]
- [NUnit.Framework.TestCaseAttribute("New", "GET", "Prefix/New", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Create", "POST", "Prefix", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Show", "GET", "Prefix/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Edit", "GET", "Prefix/{id}/Edit", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Update", "PUT", "Prefix/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Delete", "GET", "Prefix/{id}/Delete", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Destroy", "DELETE", "Prefix/{id}", new string[0])]
+ [NUnit.Framework.TestCaseAttribute("Index", "GET", "Prefix", null)]
+ [NUnit.Framework.TestCaseAttribute("New", "GET", "Prefix/New", null)]
+ [NUnit.Framework.TestCaseAttribute("Create", "POST", "Prefix", null)]
+ [NUnit.Framework.TestCaseAttribute("Show", "GET", "Prefix/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("Edit", "GET", "Prefix/{id}/Edit", null)]
+ [NUnit.Framework.TestCaseAttribute("Update", "PUT", "Prefix/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("Delete", "GET", "Prefix/{id}/Delete", null)]
+ [NUnit.Framework.TestCaseAttribute("Destroy", "DELETE", "Prefix/{id}", null)]
public virtual void GeneratingRoutesUsingTheRestfulRouteConventionOnControllersWithARoutePrefixAttribute(string action, string method, string url, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes using the RestfulRouteConvention on controllers with a RoutePre" +
@@ -114,46 +114,46 @@ public virtual void GeneratingRoutesUsingTheRestfulRouteConventionOnControllersW
#line 23
this.ScenarioSetup(scenarioInfo);
#line 24
- testRunner.Given("I have registered the routes for the RestfulRouteConventionPrefixController");
+ testRunner.Given("I have registered the routes for the RestfulRouteConventionPrefixController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 25
- testRunner.When(string.Format("I fetch the routes for the RestfulRouteConventionPrefix controller\'s {0} action", action));
+ testRunner.When(string.Format("I fetch the routes for the RestfulRouteConventionPrefix controller\'s {0} action", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 26
- testRunner.Then(string.Format("the route url is \"{0}\"", url));
+ testRunner.Then(string.Format("the route url is \"{0}\"", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 27
- testRunner.And("the default for \"controller\" is \"RestfulRouteConventionPrefix\"");
+ testRunner.And("the default for \"controller\" is \"RestfulRouteConventionPrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 28
- testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action));
+ testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 29
- testRunner.And(string.Format("the route for {0} is constrained to {1} requests", action, method));
+ testRunner.And(string.Format("the route for {0} is constrained to {1} requests", action, method), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Generating routes using the DefaultHttpRouteConvention")]
- [NUnit.Framework.TestCaseAttribute("GetAll", "GET", "DefaultHttpRouteConvention", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Get", "GET", "DefaultHttpRouteConvention/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Post", "POST", "DefaultHttpRouteConvention", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Put", "PUT", "DefaultHttpRouteConvention/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Delete", "DELETE", "DefaultHttpRouteConvention/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Custom", "GET", "DefaultHttpRouteConvention/Custom", new string[0])]
+ [NUnit.Framework.TestCaseAttribute("GetAll", "GET", "DefaultHttpRouteConvention", null)]
+ [NUnit.Framework.TestCaseAttribute("Get", "GET", "DefaultHttpRouteConvention/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("Post", "POST", "DefaultHttpRouteConvention", null)]
+ [NUnit.Framework.TestCaseAttribute("Put", "PUT", "DefaultHttpRouteConvention/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("Delete", "DELETE", "DefaultHttpRouteConvention/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("Custom", "GET", "DefaultHttpRouteConvention/Custom", null)]
public virtual void GeneratingRoutesUsingTheDefaultHttpRouteConvention(string action, string method, string url, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes using the DefaultHttpRouteConvention", exampleTags);
#line 42
this.ScenarioSetup(scenarioInfo);
#line 43
- testRunner.Given("I have registered the routes for the DefaultHttpRouteConventionController");
+ testRunner.Given("I have registered the routes for the DefaultHttpRouteConventionController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 44
- testRunner.When(string.Format("I fetch the routes for the DefaultHttpRouteConvention controller\'s {0} action", action));
+ testRunner.When(string.Format("I fetch the routes for the DefaultHttpRouteConvention controller\'s {0} action", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 45
- testRunner.Then(string.Format("the route url is \"{0}\"", url));
+ testRunner.Then(string.Format("the route url is \"{0}\"", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 46
- testRunner.And("the default for \"controller\" is \"DefaultHttpRouteConvention\"");
+ testRunner.And("the default for \"controller\" is \"DefaultHttpRouteConvention\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 47
- testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action));
+ testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 48
- testRunner.And(string.Format("the route for {0} is constrained to {1} requests", action, method));
+ testRunner.And(string.Format("the route for {0} is constrained to {1} requests", action, method), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -161,12 +161,12 @@ public virtual void GeneratingRoutesUsingTheDefaultHttpRouteConvention(string ac
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Generating routes using the DefaultHttpRouteConventionPrefix on controllers with " +
"a RoutePrefix attribute")]
- [NUnit.Framework.TestCaseAttribute("GetAll", "GET", "Prefix", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Get", "GET", "Prefix/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Post", "POST", "Prefix", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Put", "PUT", "Prefix/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Delete", "DELETE", "Prefix/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("Custom", "GET", "Prefix/Custom", new string[0])]
+ [NUnit.Framework.TestCaseAttribute("GetAll", "GET", "Prefix", null)]
+ [NUnit.Framework.TestCaseAttribute("Get", "GET", "Prefix/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("Post", "POST", "Prefix", null)]
+ [NUnit.Framework.TestCaseAttribute("Put", "PUT", "Prefix/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("Delete", "DELETE", "Prefix/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("Custom", "GET", "Prefix/Custom", null)]
public virtual void GeneratingRoutesUsingTheDefaultHttpRouteConventionPrefixOnControllersWithARoutePrefixAttribute(string action, string method, string url, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes using the DefaultHttpRouteConventionPrefix on controllers with " +
@@ -174,18 +174,18 @@ public virtual void GeneratingRoutesUsingTheDefaultHttpRouteConventionPrefixOnCo
#line 59
this.ScenarioSetup(scenarioInfo);
#line 60
- testRunner.Given("I have registered the routes for the DefaultHttpRouteConventionPrefixController");
+ testRunner.Given("I have registered the routes for the DefaultHttpRouteConventionPrefixController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 61
testRunner.When(string.Format("I fetch the routes for the DefaultHttpRouteConventionPrefix controller\'s {0} acti" +
- "on", action));
+ "on", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 62
- testRunner.Then(string.Format("the route url is \"{0}\"", url));
+ testRunner.Then(string.Format("the route url is \"{0}\"", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 63
- testRunner.And("the default for \"controller\" is \"DefaultHttpRouteConventionPrefix\"");
+ testRunner.And("the default for \"controller\" is \"DefaultHttpRouteConventionPrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 64
- testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action));
+ testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 65
- testRunner.And(string.Format("the route for {0} is constrained to {1} requests", action, method));
+ testRunner.And(string.Format("the route for {0} is constrained to {1} requests", action, method), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -201,14 +201,14 @@ public virtual void GeneratingRoutesUsingTheRestfulRouteConventionOnActionsWithA
this.ScenarioSetup(scenarioInfo);
#line 77
testRunner.Given("I have registered the routes for the RestfulRouteConventionWithExplicitRouteContr" +
- "oller");
+ "oller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 78
testRunner.When("I fetch the routes for the RestfulRouteConventionWithExplicitRoute controller\'s I" +
- "ndex action");
+ "ndex action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 79
- testRunner.Then("the 1st route url is \"RestfulRouteConventionWithExplicitRoute\"");
+ testRunner.Then("the 1st route url is \"RestfulRouteConventionWithExplicitRoute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 80
- testRunner.And("the 2nd route url is \"Legacy\"");
+ testRunner.And("the 2nd route url is \"Legacy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -224,14 +224,14 @@ public virtual void GeneratingRoutesUsingTheRestfulRouteConventionOnActionsWithA
this.ScenarioSetup(scenarioInfo);
#line 83
testRunner.Given("I have registered the routes for the RestfulRouteConventionWithExplicitOrderedRou" +
- "teController");
+ "teController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 84
testRunner.When("I fetch the routes for the RestfulRouteConventionWithExplicitOrderedRoute control" +
- "ler\'s Index action");
+ "ler\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 85
- testRunner.Then("the 1st route url is \"RestfulRouteConventionWithExplicitOrderedRoute/Primary\"");
+ testRunner.Then("the 1st route url is \"RestfulRouteConventionWithExplicitOrderedRoute/Primary\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 86
- testRunner.And("the 2nd route url is \"RestfulRouteConventionWithExplicitOrderedRoute\"");
+ testRunner.And("the 2nd route url is \"RestfulRouteConventionWithExplicitOrderedRoute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -247,14 +247,14 @@ public virtual void GeneratingRoutesUsingTheDefaultHttpRouteConventionOnActionsW
this.ScenarioSetup(scenarioInfo);
#line 89
testRunner.Given("I have registered the routes for the DefaultHttpRouteConventionWithExplicitRouteC" +
- "ontroller");
+ "ontroller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 90
testRunner.When("I fetch the routes for the DefaultHttpRouteConventionWithExplicitRoute controller" +
- "\'s Get action");
+ "\'s Get action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 91
- testRunner.Then("the 1st route url is \"DefaultHttpRouteConventionWithExplicitRoute\"");
+ testRunner.Then("the 1st route url is \"DefaultHttpRouteConventionWithExplicitRoute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 92
- testRunner.And("the 2nd route url is \"Legacy\"");
+ testRunner.And("the 2nd route url is \"Legacy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -270,15 +270,15 @@ public virtual void GeneratingRoutesUsingTheDefaultHttpRouteConventionOnActionsW
this.ScenarioSetup(scenarioInfo);
#line 95
testRunner.Given("I have registered the routes for the DefaultHttpRouteConventionWithExplicitOrdere" +
- "dRouteController");
+ "dRouteController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 96
testRunner.When("I fetch the routes for the DefaultHttpRouteConventionWithExplicitOrderedRoute con" +
- "troller\'s Get action");
+ "troller\'s Get action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 97
testRunner.Then("the 1st route url is \"DefaultHttpRouteConventionWithExplicitOrderedRoute/Primary\"" +
- "");
+ "", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 98
- testRunner.And("the 2nd route url is \"DefaultHttpRouteConventionWithExplicitOrderedRoute\"");
+ testRunner.And("the 2nd route url is \"DefaultHttpRouteConventionWithExplicitOrderedRoute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Features/RouteDefaults.feature.cs b/src/AttributeRouting.Specs/Features/RouteDefaults.feature.cs
index e8777c3..ce10e38 100644
--- a/src/AttributeRouting.Specs/Features/RouteDefaults.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteDefaults.feature.cs
@@ -1,8 +1,8 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.8.1.0
-// SpecFlow Generator Version:1.8.0.0
+// SpecFlow Version:1.9.1.84
+// SpecFlow Generator Version:1.9.0.0
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.8.1.0")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.1.84")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Route Defaults")]
@@ -68,13 +68,13 @@ public virtual void FeatureBackground()
{
#line 3
#line 4
- testRunner.Given("I have a new configuration object");
+ testRunner.Given("I have a new configuration object", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 5
- testRunner.And("I add the routes from the RouteDefaults controller");
+ testRunner.And("I add the routes from the RouteDefaults controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 6
- testRunner.And("I add the routes from the HttpRouteDefaults controller");
+ testRunner.And("I add the routes from the HttpRouteDefaults controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 7
- testRunner.And("I generate the routes with this configuration");
+ testRunner.And("I generate the routes with this configuration", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
}
@@ -88,21 +88,21 @@ public virtual void RouteDefaultSpecifiedInline()
#line 3
this.FeatureBackground();
#line 10
- testRunner.When("I fetch the routes for the RouteDefaults controller\'s InlineDefaults action");
+ testRunner.When("I fetch the routes for the RouteDefaults controller\'s InlineDefaults action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 11
- testRunner.Then("the route url is \"InlineDefaults/{hello}/{goodnight}\"");
+ testRunner.Then("the route url is \"InlineDefaults/{hello}/{goodnight}\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 12
- testRunner.Then("the default for \"hello\" is \"sun\"");
+ testRunner.Then("the default for \"hello\" is \"sun\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 13
- testRunner.Then("the default for \"goodnight\" is \"moon\"");
+ testRunner.Then("the default for \"goodnight\" is \"moon\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 14
- testRunner.When("I fetch the routes for the HttpRouteDefaults controller\'s InlineDefaults action");
+ testRunner.When("I fetch the routes for the HttpRouteDefaults controller\'s InlineDefaults action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 15
- testRunner.Then("the route url is \"InlineDefaults/{hello}/{goodnight}\"");
+ testRunner.Then("the route url is \"InlineDefaults/{hello}/{goodnight}\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 16
- testRunner.Then("the default for \"hello\" is \"sun\"");
+ testRunner.Then("the default for \"hello\" is \"sun\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 17
- testRunner.Then("the default for \"goodnight\" is \"moon\"");
+ testRunner.Then("the default for \"goodnight\" is \"moon\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -117,17 +117,17 @@ public virtual void OptionalParametersSpecifiedWithAUrlParameterToken()
#line 3
this.FeatureBackground();
#line 20
- testRunner.When("I fetch the routes for the RouteDefaults controller\'s Optionals action");
+ testRunner.When("I fetch the routes for the RouteDefaults controller\'s Optionals action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 21
- testRunner.Then("the route url is \"Optionals/{p1}\"");
+ testRunner.Then("the route url is \"Optionals/{p1}\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 22
- testRunner.And("the parameter \"p1\" is optional");
+ testRunner.And("the parameter \"p1\" is optional", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 23
- testRunner.When("I fetch the routes for the HttpRouteDefaults controller\'s Optionals action");
+ testRunner.When("I fetch the routes for the HttpRouteDefaults controller\'s Optionals action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 24
- testRunner.Then("the route url is \"Optionals/{p1}\"");
+ testRunner.Then("the route url is \"Optionals/{p1}\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 25
- testRunner.And("the parameter \"p1\" is optional");
+ testRunner.And("the parameter \"p1\" is optional", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Features/RoutePrecedence.feature.cs b/src/AttributeRouting.Specs/Features/RoutePrecedence.feature.cs
index 8c6773d..287e5a9 100644
--- a/src/AttributeRouting.Specs/Features/RoutePrecedence.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RoutePrecedence.feature.cs
@@ -1,8 +1,8 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.8.1.0
-// SpecFlow Generator Version:1.8.0.0
+// SpecFlow Version:1.9.1.84
+// SpecFlow Generator Version:1.9.0.0
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.8.1.0")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.1.84")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Route Precedence")]
@@ -72,44 +72,44 @@ public virtual void RoutePrecedenceAmongRoutesForAnActionUsingTheOrderProperty()
#line 3
this.ScenarioSetup(scenarioInfo);
#line 5
- testRunner.Given("I have registered the routes for the RoutePrecedenceAmongRoutesController");
+ testRunner.Given("I have registered the routes for the RoutePrecedenceAmongRoutesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 6
- testRunner.When("I fetch the routes for the RoutePrecedenceAmongRoutes controller\'s Index action");
+ testRunner.When("I fetch the routes for the RoutePrecedenceAmongRoutes controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 7
- testRunner.Then("the 1st route\'s url is \"Index/First\"");
+ testRunner.Then("the 1st route\'s url is \"Index/First\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 8
- testRunner.Then("the 1st route\'s url is \"Index/First\"");
+ testRunner.Then("the 1st route\'s url is \"Index/First\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 9
- testRunner.And("the 2nd route\'s url is \"Index/Second\"");
+ testRunner.And("the 2nd route\'s url is \"Index/Second\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 10
- testRunner.And("the 3rd route\'s url is \"Index/Third\"");
+ testRunner.And("the 3rd route\'s url is \"Index/Third\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 11
- testRunner.And("the 4th route\'s url is \"Index/Fourth\"");
+ testRunner.And("the 4th route\'s url is \"Index/Fourth\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 12
- testRunner.And("the 5th route\'s url is \"Index/Fifth\"");
+ testRunner.And("the 5th route\'s url is \"Index/Fifth\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 13
- testRunner.And("the 6th route\'s url is \"Index/Sixth\"");
+ testRunner.And("the 6th route\'s url is \"Index/Sixth\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 14
- testRunner.And("the 7th route\'s url is \"Index/Seventh\"");
+ testRunner.And("the 7th route\'s url is \"Index/Seventh\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 16
- testRunner.Given("I have registered the routes for the HttpRoutePrecedenceAmongRoutesController");
+ testRunner.Given("I have registered the routes for the HttpRoutePrecedenceAmongRoutesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 17
testRunner.When("I fetch the routes for the HttpRoutePrecedenceAmongRoutes controller\'s Get action" +
- "");
+ "", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 18
- testRunner.Then("the 1st route\'s url is \"Get/First\"");
+ testRunner.Then("the 1st route\'s url is \"Get/First\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 19
- testRunner.And("the 2nd route\'s url is \"Get/Second\"");
+ testRunner.And("the 2nd route\'s url is \"Get/Second\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 20
- testRunner.And("the 3rd route\'s url is \"Get/Third\"");
+ testRunner.And("the 3rd route\'s url is \"Get/Third\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 21
- testRunner.And("the 4th route\'s url is \"Get/Fourth\"");
+ testRunner.And("the 4th route\'s url is \"Get/Fourth\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 22
- testRunner.And("the 5th route\'s url is \"Get/Fifth\"");
+ testRunner.And("the 5th route\'s url is \"Get/Fifth\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 23
- testRunner.And("the 6th route\'s url is \"Get/Sixth\"");
+ testRunner.And("the 6th route\'s url is \"Get/Sixth\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 24
- testRunner.And("the 7th route\'s url is \"Get/Seventh\"");
+ testRunner.And("the 7th route\'s url is \"Get/Seventh\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -122,43 +122,43 @@ public virtual void RoutePrecedenceAmongActionsWithinAControllerUsingThePreceden
#line 26
this.ScenarioSetup(scenarioInfo);
#line 28
- testRunner.Given("I have registered the routes for the RoutePrecedenceAmongActionsController");
+ testRunner.Given("I have registered the routes for the RoutePrecedenceAmongActionsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 29
- testRunner.When("I fetch the routes for the RoutePrecedenceAmongActions controller");
+ testRunner.When("I fetch the routes for the RoutePrecedenceAmongActions controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 30
- testRunner.Then("the 1st route\'s url is \"Route0\"");
+ testRunner.Then("the 1st route\'s url is \"Route0\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 31
- testRunner.Then("the 2st route\'s url is \"Route1\"");
+ testRunner.Then("the 2st route\'s url is \"Route1\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 32
- testRunner.And("the 3nd route\'s url is \"Route2\"");
+ testRunner.And("the 3nd route\'s url is \"Route2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 33
- testRunner.And("the 4rd route\'s url is \"Route3\"");
+ testRunner.And("the 4rd route\'s url is \"Route3\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 34
- testRunner.And("the 5th route\'s url is \"Route4\"");
+ testRunner.And("the 5th route\'s url is \"Route4\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 35
- testRunner.And("the 6th route\'s url is \"Route5\"");
+ testRunner.And("the 6th route\'s url is \"Route5\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 36
- testRunner.And("the 7th route\'s url is \"Route6\"");
+ testRunner.And("the 7th route\'s url is \"Route6\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 37
- testRunner.And("the 8th route\'s url is \"Route7\"");
+ testRunner.And("the 8th route\'s url is \"Route7\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 39
- testRunner.Given("I have registered the routes for the HttpRoutePrecedenceAmongActionsController");
+ testRunner.Given("I have registered the routes for the HttpRoutePrecedenceAmongActionsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 40
- testRunner.When("I fetch the routes for the HttpRoutePrecedenceAmongActions controller");
+ testRunner.When("I fetch the routes for the HttpRoutePrecedenceAmongActions controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 41
- testRunner.Then("the 1st route\'s url is \"ApiRoute1\"");
+ testRunner.Then("the 1st route\'s url is \"ApiRoute1\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 42
- testRunner.And("the 2nd route\'s url is \"ApiRoute2\"");
+ testRunner.And("the 2nd route\'s url is \"ApiRoute2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 43
- testRunner.And("the 3rd route\'s url is \"ApiRoute3\"");
+ testRunner.And("the 3rd route\'s url is \"ApiRoute3\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 44
- testRunner.And("the 4th route\'s url is \"ApiRoute4\"");
+ testRunner.And("the 4th route\'s url is \"ApiRoute4\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 45
- testRunner.And("the 5th route\'s url is \"ApiRoute5\"");
+ testRunner.And("the 5th route\'s url is \"ApiRoute5\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 46
- testRunner.And("the 6th route\'s url is \"ApiRoute6\"");
+ testRunner.And("the 6th route\'s url is \"ApiRoute6\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 47
- testRunner.And("the 7th route\'s url is \"ApiRoute7\"");
+ testRunner.And("the 7th route\'s url is \"ApiRoute7\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -171,37 +171,37 @@ public virtual void RoutePrecedenceSetForTheSiteUsingTheSitePrecedenceProperty()
#line 49
this.ScenarioSetup(scenarioInfo);
#line 52
- testRunner.Given("I have a new configuration object");
+ testRunner.Given("I have a new configuration object", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 53
- testRunner.And("I add the routes from the RoutePrecedenceAmongControllers1 controller");
+ testRunner.And("I add the routes from the RoutePrecedenceAmongControllers1 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 54
- testRunner.And("I add the routes from the RoutePrecedenceAmongTheSitesRoutes controller");
+ testRunner.And("I add the routes from the RoutePrecedenceAmongTheSitesRoutes controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 55
- testRunner.When("I generate the routes with this configuration");
+ testRunner.When("I generate the routes with this configuration", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 56
- testRunner.And("I fetch all the routes");
+ testRunner.And("I fetch all the routes", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 57
- testRunner.Then("the 1st route\'s url is \"The-First-Route\"");
+ testRunner.Then("the 1st route\'s url is \"The-First-Route\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 58
- testRunner.Then("the 2nd route\'s url is \"Controller1/Index\"");
+ testRunner.Then("the 2nd route\'s url is \"Controller1/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 59
- testRunner.Then("the 3rd route\'s url is \"The-Last-Route\"");
+ testRunner.Then("the 3rd route\'s url is \"The-Last-Route\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 61
- testRunner.Given("I have a new configuration object");
+ testRunner.Given("I have a new configuration object", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 62
- testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers1 controller");
+ testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers1 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 63
- testRunner.And("I add the routes from the HttpRoutePrecedenceAmongTheSitesRoutes controller");
+ testRunner.And("I add the routes from the HttpRoutePrecedenceAmongTheSitesRoutes controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 64
- testRunner.When("I generate the routes with this configuration");
+ testRunner.When("I generate the routes with this configuration", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 65
- testRunner.And("I fetch all the routes");
+ testRunner.And("I fetch all the routes", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 66
- testRunner.Then("the 1st route\'s url is \"The-First-Route\"");
+ testRunner.Then("the 1st route\'s url is \"The-First-Route\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 67
- testRunner.Then("the 2nd route\'s url is \"ApiController1/Get\"");
+ testRunner.Then("the 2nd route\'s url is \"ApiController1/Get\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 68
- testRunner.Then("the 3rd route\'s url is \"The-Last-Route\"");
+ testRunner.Then("the 3rd route\'s url is \"The-Last-Route\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -214,42 +214,42 @@ public virtual void RoutePrecedenceSetViaOrderPrecedenceAndSitePrecedencePropert
#line 70
this.ScenarioSetup(scenarioInfo);
#line 72
- testRunner.Given("I have registered the routes for the RoutePrecedenceViaRoutePropertiesController");
+ testRunner.Given("I have registered the routes for the RoutePrecedenceViaRoutePropertiesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 73
- testRunner.When("I fetch all the routes");
+ testRunner.When("I fetch all the routes", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 74
- testRunner.Then("the 1st route\'s url is \"Route1\"");
+ testRunner.Then("the 1st route\'s url is \"Route1\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 75
- testRunner.And("the 2nd route\'s url is \"Route2\"");
+ testRunner.And("the 2nd route\'s url is \"Route2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 76
- testRunner.And("the 3rd route\'s url is \"Route3\"");
+ testRunner.And("the 3rd route\'s url is \"Route3\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 77
- testRunner.And("the 4th route\'s url is \"Route4\"");
+ testRunner.And("the 4th route\'s url is \"Route4\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 78
- testRunner.And("the 5th route\'s url is \"Route5\"");
+ testRunner.And("the 5th route\'s url is \"Route5\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 79
- testRunner.And("the 6th route\'s url is \"Route6\"");
+ testRunner.And("the 6th route\'s url is \"Route6\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 80
- testRunner.And("the 7th route\'s url is \"Route7\"");
+ testRunner.And("the 7th route\'s url is \"Route7\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 82
testRunner.Given("I have registered the routes for the HttpRoutePrecedenceViaRoutePropertiesControl" +
- "ler");
+ "ler", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 83
- testRunner.When("I fetch all the routes");
+ testRunner.When("I fetch all the routes", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 84
- testRunner.Then("the 1st route\'s url is \"ApiRoute1\"");
+ testRunner.Then("the 1st route\'s url is \"ApiRoute1\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 85
- testRunner.And("the 2nd route\'s url is \"ApiRoute2\"");
+ testRunner.And("the 2nd route\'s url is \"ApiRoute2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 86
- testRunner.And("the 3rd route\'s url is \"ApiRoute3\"");
+ testRunner.And("the 3rd route\'s url is \"ApiRoute3\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 87
- testRunner.And("the 4th route\'s url is \"ApiRoute4\"");
+ testRunner.And("the 4th route\'s url is \"ApiRoute4\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 88
- testRunner.And("the 5th route\'s url is \"ApiRoute5\"");
+ testRunner.And("the 5th route\'s url is \"ApiRoute5\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 89
- testRunner.And("the 6th route\'s url is \"ApiRoute6\"");
+ testRunner.And("the 6th route\'s url is \"ApiRoute6\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 90
- testRunner.And("the 7th route\'s url is \"ApiRoute7\"");
+ testRunner.And("the 7th route\'s url is \"ApiRoute7\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -264,69 +264,69 @@ public virtual void RoutePrecedenceAmongControllersAddedIndividuallyUsingTheConf
#line 92
this.ScenarioSetup(scenarioInfo);
#line 94
- testRunner.Given("I have a new configuration object");
+ testRunner.Given("I have a new configuration object", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 95
- testRunner.And("I add the routes from the RoutePrecedenceAmongControllers1 controller");
+ testRunner.And("I add the routes from the RoutePrecedenceAmongControllers1 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 96
- testRunner.And("I add the routes from the RoutePrecedenceAmongControllers2 controller");
+ testRunner.And("I add the routes from the RoutePrecedenceAmongControllers2 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 97
- testRunner.And("I add the routes from the RoutePrecedenceAmongControllers3 controller");
+ testRunner.And("I add the routes from the RoutePrecedenceAmongControllers3 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 98
- testRunner.And("I add the Mvc routes from the executing assembly");
+ testRunner.And("I add the Mvc routes from the executing assembly", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 99
- testRunner.And("I add the routes from the RoutePrecedenceAmongControllers4 controller");
+ testRunner.And("I add the routes from the RoutePrecedenceAmongControllers4 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 100
- testRunner.And("I add the routes from the RoutePrecedenceAmongControllers5 controller");
+ testRunner.And("I add the routes from the RoutePrecedenceAmongControllers5 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 101
- testRunner.When("I generate the routes with this configuration");
+ testRunner.When("I generate the routes with this configuration", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 102
testRunner.Then("the routes from the RoutePrecedenceAmongControllers1 controller precede those fro" +
- "m the RoutePrecedenceAmongControllers2 controller");
+ "m the RoutePrecedenceAmongControllers2 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 103
testRunner.And("the routes from the RoutePrecedenceAmongControllers2 controller precede those fro" +
- "m the RoutePrecedenceAmongControllers3 controller");
+ "m the RoutePrecedenceAmongControllers3 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 104
testRunner.And("the routes from the RoutePrecedenceAmongControllers3 controller precede those fro" +
- "m the StandardUsage controller");
+ "m the StandardUsage controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 105
testRunner.And("the routes from the StandardUsage controller precede those from the RoutePreceden" +
- "ceAmongControllers4 controller");
+ "ceAmongControllers4 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 106
testRunner.And("the routes from the RoutePrecedenceAmongControllers4 controller precede those fro" +
- "m the RoutePrecedenceAmongControllers5 controller");
+ "m the RoutePrecedenceAmongControllers5 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 107
- testRunner.And("no routes follow the routes from the RoutePrecedenceAmongControllers5 controller");
+ testRunner.And("no routes follow the routes from the RoutePrecedenceAmongControllers5 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 109
- testRunner.Given("I have a new configuration object");
+ testRunner.Given("I have a new configuration object", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 110
- testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers1 controller");
+ testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers1 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 111
- testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers2 controller");
+ testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers2 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 112
- testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers3 controller");
+ testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers3 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 113
- testRunner.And("I add the Http routes from the executing assembly");
+ testRunner.And("I add the Http routes from the executing assembly", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 114
- testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers4 controller");
+ testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers4 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 115
- testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers5 controller");
+ testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers5 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 116
- testRunner.When("I generate the routes with this configuration");
+ testRunner.When("I generate the routes with this configuration", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 117
testRunner.Then("the routes from the HttpRoutePrecedenceAmongControllers1 controller precede those" +
- " from the HttpRoutePrecedenceAmongControllers2 controller");
+ " from the HttpRoutePrecedenceAmongControllers2 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 118
testRunner.And("the routes from the HttpRoutePrecedenceAmongControllers2 controller precede those" +
- " from the HttpRoutePrecedenceAmongControllers3 controller");
+ " from the HttpRoutePrecedenceAmongControllers3 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 119
testRunner.And("the routes from the HttpStandardUsage controller precede those from the HttpRoute" +
- "PrecedenceAmongControllers4 controller");
+ "PrecedenceAmongControllers4 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 120
testRunner.And("the routes from the HttpRoutePrecedenceAmongControllers4 controller precede those" +
- " from the HttpRoutePrecedenceAmongControllers5 controller");
+ " from the HttpRoutePrecedenceAmongControllers5 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 121
testRunner.And("no routes follow the routes from the HttpRoutePrecedenceAmongControllers5 control" +
- "ler");
+ "ler", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -341,35 +341,35 @@ public virtual void RoutePrecedenceAmongControllersAddedByBaseTypeUsingTheConfig
#line 123
this.ScenarioSetup(scenarioInfo);
#line 125
- testRunner.Given("I have a new configuration object");
+ testRunner.Given("I have a new configuration object", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 126
testRunner.And("I add the routes from controllers derived from the RoutePrecedenceAmongDerivedCon" +
- "trollersBase controller");
+ "trollersBase controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 127
- testRunner.And("I add the routes from the RoutePrecedenceAmongControllers1 controller");
+ testRunner.And("I add the routes from the RoutePrecedenceAmongControllers1 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 128
- testRunner.When("I generate the routes with this configuration");
+ testRunner.When("I generate the routes with this configuration", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 129
testRunner.Then("the routes from the RoutePrecedenceAmongDerivedControllers1 controller precede th" +
- "ose from the RoutePrecedenceAmongControllers1 controller");
+ "ose from the RoutePrecedenceAmongControllers1 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 130
testRunner.And("the routes from the RoutePrecedenceAmongDerivedControllers2 controller precede th" +
- "ose from the RoutePrecedenceAmongControllers1 controller");
+ "ose from the RoutePrecedenceAmongControllers1 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 132
- testRunner.Given("I have a new configuration object");
+ testRunner.Given("I have a new configuration object", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 133
testRunner.And("I add the routes from controllers derived from the HttpRoutePrecedenceAmongDerive" +
- "dControllersBase controller");
+ "dControllersBase controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 134
- testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers1 controller");
+ testRunner.And("I add the routes from the HttpRoutePrecedenceAmongControllers1 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 135
- testRunner.When("I generate the routes with this configuration");
+ testRunner.When("I generate the routes with this configuration", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 136
testRunner.Then("the routes from the HttpRoutePrecedenceAmongDerivedControllers1 controller preced" +
- "e those from the HttpRoutePrecedenceAmongControllers1 controller");
+ "e those from the HttpRoutePrecedenceAmongControllers1 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 137
testRunner.And("the routes from the HttpRoutePrecedenceAmongDerivedControllers2 controller preced" +
- "e those from the HttpRoutePrecedenceAmongControllers1 controller");
+ "e those from the HttpRoutePrecedenceAmongControllers1 controller", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs
index 07d92d5..d5cc48d 100644
--- a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs
@@ -1,8 +1,8 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.8.1.0
-// SpecFlow Generator Version:1.8.0.0
+// SpecFlow Version:1.9.1.84
+// SpecFlow Generator Version:1.9.0.0
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.8.1.0")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.1.84")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Route Prefixes")]
@@ -72,17 +72,17 @@ public virtual void GeneratingPrefixedRoutes()
#line 3
this.ScenarioSetup(scenarioInfo);
#line 5
- testRunner.Given("I have registered the routes for the RoutePrefixesController");
+ testRunner.Given("I have registered the routes for the RoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 6
- testRunner.When("I fetch the routes for the RoutePrefixes controller\'s Index action");
+ testRunner.When("I fetch the routes for the RoutePrefixes controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 7
- testRunner.Then("the route url is \"Prefix/Index\"");
+ testRunner.Then("the route url is \"Prefix/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 9
- testRunner.Given("I have registered the routes for the HttpRoutePrefixesController");
+ testRunner.Given("I have registered the routes for the HttpRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 10
- testRunner.When("I fetch the routes for the HttpRoutePrefixes controller\'s Get action");
+ testRunner.When("I fetch the routes for the HttpRoutePrefixes controller\'s Get action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 11
- testRunner.Then("the route url is \"ApiPrefix/Get\"");
+ testRunner.Then("the route url is \"ApiPrefix/Get\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -95,17 +95,17 @@ public virtual void GeneratingPrefixedRoutesWhenRouteUrlsSpecifyADuplicatePrefix
#line 13
this.ScenarioSetup(scenarioInfo);
#line 15
- testRunner.Given("I have registered the routes for the RoutePrefixesController");
+ testRunner.Given("I have registered the routes for the RoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 16
- testRunner.When("I fetch the routes for the RoutePrefixes controller\'s DuplicatePrefix action");
+ testRunner.When("I fetch the routes for the RoutePrefixes controller\'s DuplicatePrefix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 17
- testRunner.Then("the route url is \"Prefix/DuplicatePrefix\"");
+ testRunner.Then("the route url is \"Prefix/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 19
- testRunner.Given("I have registered the routes for the HttpRoutePrefixesController");
+ testRunner.Given("I have registered the routes for the HttpRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 20
- testRunner.When("I fetch the routes for the HttpRoutePrefixes controller\'s DuplicatePrefix action");
+ testRunner.When("I fetch the routes for the HttpRoutePrefixes controller\'s DuplicatePrefix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 21
- testRunner.Then("the route url is \"ApiPrefix/DuplicatePrefix\"");
+ testRunner.Then("the route url is \"ApiPrefix/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -118,17 +118,17 @@ public virtual void GeneratingAbsoluteRoutesWhenARoutePrefixIsDefined()
#line 23
this.ScenarioSetup(scenarioInfo);
#line 25
- testRunner.Given("I have registered the routes for the RoutePrefixesController");
+ testRunner.Given("I have registered the routes for the RoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 26
- testRunner.When("I fetch the routes for the RoutePrefixes controller\'s Absolute action");
+ testRunner.When("I fetch the routes for the RoutePrefixes controller\'s Absolute action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 27
- testRunner.Then("the route url is \"PrefixAbsolute\"");
+ testRunner.Then("the route url is \"PrefixAbsolute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 29
- testRunner.Given("I have registered the routes for the HttpRoutePrefixesController");
+ testRunner.Given("I have registered the routes for the HttpRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 30
- testRunner.When("I fetch the routes for the HttpRoutePrefixes controller\'s Absolute action");
+ testRunner.When("I fetch the routes for the HttpRoutePrefixes controller\'s Absolute action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 31
- testRunner.Then("the route url is \"ApiPrefixAbsolute\"");
+ testRunner.Then("the route url is \"ApiPrefixAbsolute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -141,19 +141,19 @@ public virtual void GeneratingPrefixedRoutesWhenRouteUrlStartsWithTheRoutePrefix
#line 33
this.ScenarioSetup(scenarioInfo);
#line 35
- testRunner.Given("I have registered the routes for the RoutePrefixesController");
+ testRunner.Given("I have registered the routes for the RoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 36
testRunner.When("I fetch the routes for the RoutePrefixes controller\'s RouteBeginsWithRoutePrefix " +
- "action");
+ "action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 37
- testRunner.Then("the route url is \"Prefix/Prefixer\"");
+ testRunner.Then("the route url is \"Prefix/Prefixer\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 39
- testRunner.Given("I have registered the routes for the HttpRoutePrefixesController");
+ testRunner.Given("I have registered the routes for the HttpRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 40
testRunner.When("I fetch the routes for the HttpRoutePrefixes controller\'s RouteBeginsWithRoutePre" +
- "fix action");
+ "fix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 41
- testRunner.Then("the route url is \"ApiPrefix/ApiPrefixer\"");
+ testRunner.Then("the route url is \"ApiPrefix/ApiPrefixer\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -166,17 +166,17 @@ public virtual void GeneratingPrefixedAreaRoutes()
#line 43
this.ScenarioSetup(scenarioInfo);
#line 45
- testRunner.Given("I have registered the routes for the AreaRoutePrefixesController");
+ testRunner.Given("I have registered the routes for the AreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 46
- testRunner.When("I fetch the routes for the AreaRoutePrefixes controller\'s Index action");
+ testRunner.When("I fetch the routes for the AreaRoutePrefixes controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 47
- testRunner.Then("the route url is \"Area/Prefix/Index\"");
+ testRunner.Then("the route url is \"Area/Prefix/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 49
- testRunner.Given("I have registered the routes for the HttpAreaRoutePrefixesController");
+ testRunner.Given("I have registered the routes for the HttpAreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 50
- testRunner.When("I fetch the routes for the HttpAreaRoutePrefixes controller\'s Get action");
+ testRunner.When("I fetch the routes for the HttpAreaRoutePrefixes controller\'s Get action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 51
- testRunner.Then("the route url is \"ApiArea/ApiPrefix/Get\"");
+ testRunner.Then("the route url is \"ApiArea/ApiPrefix/Get\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -189,18 +189,18 @@ public virtual void GeneratingPrefixedAreaRoutesWhenRouteUrlsSpecifyADuplicatePr
#line 53
this.ScenarioSetup(scenarioInfo);
#line 55
- testRunner.Given("I have registered the routes for the AreaRoutePrefixesController");
+ testRunner.Given("I have registered the routes for the AreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 56
- testRunner.When("I fetch the routes for the AreaRoutePrefixes controller\'s DuplicatePrefix action");
+ testRunner.When("I fetch the routes for the AreaRoutePrefixes controller\'s DuplicatePrefix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 57
- testRunner.Then("the route url is \"Area/Prefix/DuplicatePrefix\"");
+ testRunner.Then("the route url is \"Area/Prefix/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 59
- testRunner.Given("I have registered the routes for the HttpAreaRoutePrefixesController");
+ testRunner.Given("I have registered the routes for the HttpAreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 60
testRunner.When("I fetch the routes for the HttpAreaRoutePrefixes controller\'s DuplicatePrefix act" +
- "ion");
+ "ion", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 61
- testRunner.Then("the route url is \"ApiArea/ApiPrefix/DuplicatePrefix\"");
+ testRunner.Then("the route url is \"ApiArea/ApiPrefix/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -213,17 +213,17 @@ public virtual void GeneratingAbsoluteRoutesWhenARouteAreaAndRoutePrefixIsDefine
#line 63
this.ScenarioSetup(scenarioInfo);
#line 65
- testRunner.Given("I have registered the routes for the AreaRoutePrefixesController");
+ testRunner.Given("I have registered the routes for the AreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 66
- testRunner.When("I fetch the routes for the AreaRoutePrefixes controller\'s Absolute action");
+ testRunner.When("I fetch the routes for the AreaRoutePrefixes controller\'s Absolute action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 67
- testRunner.Then("the route url is \"AreaPrefixAbsolute\"");
+ testRunner.Then("the route url is \"AreaPrefixAbsolute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 69
- testRunner.Given("I have registered the routes for the HttpAreaRoutePrefixesController");
+ testRunner.Given("I have registered the routes for the HttpAreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 70
- testRunner.When("I fetch the routes for the HttpAreaRoutePrefixes controller\'s Absolute action");
+ testRunner.When("I fetch the routes for the HttpAreaRoutePrefixes controller\'s Absolute action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 71
- testRunner.Then("the route url is \"ApiAreaPrefixAbsolute\"");
+ testRunner.Then("the route url is \"ApiAreaPrefixAbsolute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -238,19 +238,19 @@ public virtual void GeneratingRoutesWhenARouteAreaAndRoutePrefixAreDefinedAndThe
#line 73
this.ScenarioSetup(scenarioInfo);
#line 75
- testRunner.Given("I have registered the routes for the AreaRoutePrefixesController");
+ testRunner.Given("I have registered the routes for the AreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 76
testRunner.When("I fetch the routes for the AreaRoutePrefixes controller\'s RelativeUrlIsAreaUrl ac" +
- "tion");
+ "tion", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 77
- testRunner.Then("the route url is \"Area/Prefix/Area\"");
+ testRunner.Then("the route url is \"Area/Prefix/Area\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 79
- testRunner.Given("I have registered the routes for the HttpAreaRoutePrefixesController");
+ testRunner.Given("I have registered the routes for the HttpAreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 80
testRunner.When("I fetch the routes for the HttpAreaRoutePrefixes controller\'s RelativeUrlIsAreaUr" +
- "l action");
+ "l action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 81
- testRunner.Then("the route url is \"ApiArea/ApiPrefix/ApiArea\"");
+ testRunner.Then("the route url is \"ApiArea/ApiPrefix/ApiArea\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Features/StandardUsage.feature.cs b/src/AttributeRouting.Specs/Features/StandardUsage.feature.cs
index 0f4a636..c7b3042 100644
--- a/src/AttributeRouting.Specs/Features/StandardUsage.feature.cs
+++ b/src/AttributeRouting.Specs/Features/StandardUsage.feature.cs
@@ -1,8 +1,8 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.8.1.0
-// SpecFlow Generator Version:1.8.0.0
+// SpecFlow Version:1.9.1.84
+// SpecFlow Generator Version:1.9.0.0
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.8.1.0")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.1.84")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Standard Usage")]
@@ -66,68 +66,68 @@ public virtual void ScenarioCleanup()
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Generating routes for an MVC controller")]
- [NUnit.Framework.TestCaseAttribute("GET", "Index", "", new string[0])]
- [NUnit.Framework.TestCaseAttribute("HEAD", "Index", "", new string[0])]
- [NUnit.Framework.TestCaseAttribute("POST", "Create", "Create", new string[0])]
- [NUnit.Framework.TestCaseAttribute("PUT", "Update", "Update/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("DELETE", "Destroy", "Destroy/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("GET", "Wildcards", "Wildcards/{*pathInfo}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("", "AnyVerb", "AnyVerb", new string[0])]
+ [NUnit.Framework.TestCaseAttribute("GET", "Index", "", null)]
+ [NUnit.Framework.TestCaseAttribute("HEAD", "Index", "", null)]
+ [NUnit.Framework.TestCaseAttribute("POST", "Create", "Create", null)]
+ [NUnit.Framework.TestCaseAttribute("PUT", "Update", "Update/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("DELETE", "Destroy", "Destroy/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("GET", "Wildcards", "Wildcards/{*pathInfo}", null)]
+ [NUnit.Framework.TestCaseAttribute("", "AnyVerb", "AnyVerb", null)]
public virtual void GeneratingRoutesForAnMVCController(string method, string action, string url, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes for an MVC controller", exampleTags);
#line 3
this.ScenarioSetup(scenarioInfo);
#line 4
- testRunner.Given("I have registered the routes for the StandardUsageController");
+ testRunner.Given("I have registered the routes for the StandardUsageController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 5
- testRunner.When(string.Format("I fetch the routes for the StandardUsageController\'s {0} action", action));
+ testRunner.When(string.Format("I fetch the routes for the StandardUsageController\'s {0} action", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 6
- testRunner.Then(string.Format("the route is constrained to {0} requests", method));
+ testRunner.Then(string.Format("the route is constrained to {0} requests", method), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 7
- testRunner.And(string.Format("the route url is \"{0}\"", url));
+ testRunner.And(string.Format("the route url is \"{0}\"", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 8
- testRunner.And("the default for \"controller\" is \"StandardUsage\"");
+ testRunner.And("the default for \"controller\" is \"StandardUsage\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 9
- testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action));
+ testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 10
- testRunner.And("the namespace is \"AttributeRouting.Specs.Subjects\"");
+ testRunner.And("the namespace is \"AttributeRouting.Specs.Subjects\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Generating routes for an API controller")]
- [NUnit.Framework.TestCaseAttribute("GET", "Get", "api", new string[0])]
- [NUnit.Framework.TestCaseAttribute("HEAD", "Get", "api", new string[0])]
- [NUnit.Framework.TestCaseAttribute("OPTIONS", "Get", "api", new string[0])]
- [NUnit.Framework.TestCaseAttribute("POST", "Post", "api", new string[0])]
- [NUnit.Framework.TestCaseAttribute("OPTIONS", "Post", "api", new string[0])]
- [NUnit.Framework.TestCaseAttribute("PUT", "Put", "api/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("OPTIONS", "Put", "api/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("DELETE", "Delete", "api/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("OPTIONS", "Delete", "api/{id}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("GET", "Wildcards", "api/Wildcards/{*pathInfo}", new string[0])]
- [NUnit.Framework.TestCaseAttribute("", "AnyVerb", "api/AnyVerb", new string[0])]
+ [NUnit.Framework.TestCaseAttribute("GET", "Get", "api", null)]
+ [NUnit.Framework.TestCaseAttribute("HEAD", "Get", "api", null)]
+ [NUnit.Framework.TestCaseAttribute("OPTIONS", "Get", "api", null)]
+ [NUnit.Framework.TestCaseAttribute("POST", "Post", "api", null)]
+ [NUnit.Framework.TestCaseAttribute("OPTIONS", "Post", "api", null)]
+ [NUnit.Framework.TestCaseAttribute("PUT", "Put", "api/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("OPTIONS", "Put", "api/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("DELETE", "Delete", "api/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("OPTIONS", "Delete", "api/{id}", null)]
+ [NUnit.Framework.TestCaseAttribute("GET", "Wildcards", "api/Wildcards/{*pathInfo}", null)]
+ [NUnit.Framework.TestCaseAttribute("", "AnyVerb", "api/AnyVerb", null)]
public virtual void GeneratingRoutesForAnAPIController(string method, string action, string url, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes for an API controller", exampleTags);
#line 22
this.ScenarioSetup(scenarioInfo);
#line 23
- testRunner.Given("I have registered the routes for the HttpStandardUsageController");
+ testRunner.Given("I have registered the routes for the HttpStandardUsageController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 24
- testRunner.When(string.Format("I fetch the routes for the HttpStandardUsageController\'s {0} action", action));
+ testRunner.When(string.Format("I fetch the routes for the HttpStandardUsageController\'s {0} action", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 25
- testRunner.Then(string.Format("the route is constrained to {0} requests", method));
+ testRunner.Then(string.Format("the route is constrained to {0} requests", method), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 26
- testRunner.And(string.Format("the route url is \"{0}\"", url));
+ testRunner.And(string.Format("the route url is \"{0}\"", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 27
- testRunner.And("the default for \"controller\" is \"HttpStandardUsage\"");
+ testRunner.And("the default for \"controller\" is \"HttpStandardUsage\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 28
- testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action));
+ testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 29
- testRunner.And("the namespace is \"AttributeRouting.Specs.Subjects.Http\"");
+ testRunner.And("the namespace is \"AttributeRouting.Specs.Subjects.Http\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -140,11 +140,11 @@ public virtual void RespondingToOPTIONSRequestsInAnAPIController()
#line 45
this.ScenarioSetup(scenarioInfo);
#line 46
- testRunner.Given("I have registered the routes for the HttpStandardUsageController");
+ testRunner.Given("I have registered the routes for the HttpStandardUsageController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 47
- testRunner.When("an OPTIONS request for \"api\" is made");
+ testRunner.When("an OPTIONS request for \"api\" is made", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 48
- testRunner.Then("the Get action is matched");
+ testRunner.Then("the Get action is matched", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Subjects/Http/HttpInlineRouteConstraintsControllers.cs b/src/AttributeRouting.Specs/Subjects/Http/HttpInlineRouteConstraintsControllers.cs
index 651b457..2d10b6e 100644
--- a/src/AttributeRouting.Specs/Subjects/Http/HttpInlineRouteConstraintsControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/Http/HttpInlineRouteConstraintsControllers.cs
@@ -135,6 +135,12 @@ public string Regex(int x)
return "";
}
+ [GET(@"RegexRange/{x:regex(\w{1,8})}")]
+ public string RegexRange(string x)
+ {
+ return x;
+ }
+
[GET("Compound/{x:int:max(10)}")]
public string Compound(int x)
{
diff --git a/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs b/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs
index 9b4e6c6..44ee752 100644
--- a/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs
@@ -135,6 +135,12 @@ public string Regex(string x)
return x;
}
+ [GET(@"RegexRange/{x:regex(\w{1,8})}")]
+ public string RegexRange(string x)
+ {
+ return x;
+ }
+
[GET("Compound/{x:int:max(10)}")]
public string Compound(int x)
{
diff --git a/src/AttributeRouting.Specs/app.config b/src/AttributeRouting.Specs/app.config
index 59e2304..f5d5a98 100644
--- a/src/AttributeRouting.Specs/app.config
+++ b/src/AttributeRouting.Specs/app.config
@@ -12,6 +12,6 @@
-
+
\ No newline at end of file
diff --git a/src/AttributeRouting.Specs/packages.config b/src/AttributeRouting.Specs/packages.config
index 929efc5..a5c49be 100644
--- a/src/AttributeRouting.Specs/packages.config
+++ b/src/AttributeRouting.Specs/packages.config
@@ -11,5 +11,5 @@
-
+
\ No newline at end of file
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 7d36919..66e2ff7 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -232,7 +232,14 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
// Constraint of the form "firstName:string(50)"
var indexOfOpenParen = definition.IndexOf('(');
constraintName = definition.Substring(0, indexOfOpenParen);
- var constraintParams = definition.Substring(indexOfOpenParen + 1, definition.Length - indexOfOpenParen - 2).SplitAndTrim(",");
+
+ // Parse constraint params.
+ // NOTE: Splitting on commas only applies to non-regex constraints.
+ var constraintParamsRaw = definition.Substring(indexOfOpenParen + 1, definition.Length - indexOfOpenParen - 2);
+ var constraintParams = constraintName.ValueEquals("regex")
+ ? new[] {constraintParamsRaw}
+ : constraintParamsRaw.SplitAndTrim(",");
+
constraint = constraintFactory.CreateInlineRouteConstraint(constraintName, constraintParams);
}
else
From 2540cb9a4885136ef270c5d6eb46f544d714b4e3 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Thu, 29 Nov 2012 13:22:29 -0700
Subject: [PATCH 02/97] #132 - basic support for constraints in the
querystring.
---
.../Features/RouteConstraints.feature | 14 +++
.../Features/RouteConstraints.feature.cs | 107 ++++++++++++------
.../Steps/RouteConstraintSteps.cs | 12 +-
.../Steps/SharedSteps.cs | 9 +-
.../HttpInlineRouteConstraintsControllers.cs | 6 +
.../InlineRouteConstraintsControllers.cs | 6 +
.../AttributeRouting.Tests.Web.csproj | 5 +
.../QueryStringConstraintController.cs | 20 ++++
.../Factories/RouteConstraintFactory.cs | 5 +
.../Factories/RouteConstraintFactory.cs | 5 +
.../AttributeRouting.Web.Http.csproj | 2 +
.../QueryStringRouteConstraintWrapper.cs | 40 +++++++
.../Factories/RouteConstraintFactory.cs | 5 +
.../AttributeRouting.Web.csproj | 1 +
.../QueryStringRouteConstraintWrapper.cs | 38 +++++++
src/AttributeRouting/AttributeRouting.csproj | 1 +
.../IQueryStringRouteConstraintWrapper.cs | 21 ++++
.../Factories/IRouteConstraintFactory.cs | 6 +
.../Framework/RouteBuilder.cs | 78 ++++++++++---
19 files changed, 326 insertions(+), 55 deletions(-)
create mode 100644 src/AttributeRouting.Tests.Web/Controllers/QueryStringConstraintController.cs
create mode 100644 src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraintWrapper.cs
create mode 100644 src/AttributeRouting.Web/Constraints/QueryStringRouteConstraintWrapper.cs
create mode 100644 src/AttributeRouting/Constraints/IQueryStringRouteConstraintWrapper.cs
diff --git a/src/AttributeRouting.Specs/Features/RouteConstraints.feature b/src/AttributeRouting.Specs/Features/RouteConstraints.feature
index c8aea4e..c8db6d5 100644
--- a/src/AttributeRouting.Specs/Features/RouteConstraints.feature
+++ b/src/AttributeRouting.Specs/Features/RouteConstraints.feature
@@ -34,6 +34,18 @@ Scenario Outline: Inline constraints
| Compound | IntRouteConstraint |
| Compound | MaxRouteConstraint |
+Scenario: Inline constraints in the querystring
+ # MVC
+ Given I have registered the routes for the InlineRouteConstraintsController
+ When I fetch the routes for the InlineRouteConstraints controller's Querystring action
+ Then the route url is "Inline-Constraints/Querystring"
+ And the parameter "x" is constrained by an inline AttributeRouting.Web.Constraints.IntRouteConstraint
+ # Web API
+ Given I have registered the routes for the HttpInlineRouteConstraintsController
+ When I fetch the routes for the HttpInlineRouteConstraints controller's Querystring action
+ Then the route url is "Http-Inline-Constraints/Querystring"
+ And the parameter "x" is constrained by an inline AttributeRouting.Web.Constraints.IntRouteConstraint
+
Scenario: Multiple inline constraints per url segment
# MVC
Given I have registered the routes for the InlineRouteConstraintsController
@@ -131,3 +143,5 @@ Scenario Outline: Matching inline route constraints
| Enum/taupe | Enum | is not |
| WithOptional | WithOptional | is |
| WithDefault | WithDefault | is |
+ | Querystring?x=123 | Querystring | is |
+ | Querystring?x=abc | Querystring | is not |
diff --git a/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs b/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
index e13d6d2..ea7a00b 100644
--- a/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
@@ -3,7 +3,7 @@
// This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:1.9.0.77
// SpecFlow Generator Version:1.9.0.0
-// Runtime Version:4.0.30319.17929
+// Runtime Version:4.0.30319.18010
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -114,36 +114,67 @@ public virtual void InlineConstraints(string actionName, string constraintTypeNa
}
[NUnit.Framework.TestAttribute()]
- [NUnit.Framework.DescriptionAttribute("Multiple inline constraints per url segment")]
- public virtual void MultipleInlineConstraintsPerUrlSegment()
+ [NUnit.Framework.DescriptionAttribute("Inline constraints in the querystring")]
+ public virtual void InlineConstraintsInTheQuerystring()
{
- TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Multiple inline constraints per url segment", ((string[])(null)));
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Inline constraints in the querystring", ((string[])(null)));
#line 37
this.ScenarioSetup(scenarioInfo);
#line 39
testRunner.Given("I have registered the routes for the InlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 40
+ testRunner.When("I fetch the routes for the InlineRouteConstraints controller\'s Querystring action" +
+ "", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 41
+ testRunner.Then("the route url is \"Inline-Constraints/Querystring\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 42
+ testRunner.And("the parameter \"x\" is constrained by an inline AttributeRouting.Web.Constraints.In" +
+ "tRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 44
+ testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 45
+ testRunner.When("I fetch the routes for the HttpInlineRouteConstraints controller\'s Querystring ac" +
+ "tion", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 46
+ testRunner.Then("the route url is \"Http-Inline-Constraints/Querystring\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 47
+ testRunner.And("the parameter \"x\" is constrained by an inline AttributeRouting.Web.Constraints.In" +
+ "tRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Multiple inline constraints per url segment")]
+ public virtual void MultipleInlineConstraintsPerUrlSegment()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Multiple inline constraints per url segment", ((string[])(null)));
+#line 49
+this.ScenarioSetup(scenarioInfo);
+#line 51
+ testRunner.Given("I have registered the routes for the InlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 52
testRunner.When("I fetch the routes for the InlineRouteConstraints controller\'s MultipleWithinUrlS" +
"egment action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 41
+#line 53
testRunner.Then("the route url is \"Inline-Constraints/avatar/{width}x{height}/{image}\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 42
+#line 54
testRunner.And("the parameter \"width\" is constrained by an inline AttributeRouting.Web.Constraint" +
"s.IntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 43
+#line 55
testRunner.And("the parameter \"height\" is constrained by an inline AttributeRouting.Web.Constrain" +
"ts.IntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 45
+#line 57
testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 46
+#line 58
testRunner.When("I fetch the routes for the HttpInlineRouteConstraints controller\'s MultipleWithin" +
"UrlSegment action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 47
+#line 59
testRunner.Then("the route url is \"Http-Inline-Constraints/avatar/{width}x{height}/{image}\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 48
+#line 60
testRunner.And("the parameter \"width\" is constrained by an inline AttributeRouting.Web.Constraint" +
"s.IntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 49
+#line 61
testRunner.And("the parameter \"height\" is constrained by an inline AttributeRouting.Web.Constrain" +
"ts.IntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
@@ -155,27 +186,27 @@ public virtual void MultipleInlineConstraintsPerUrlSegment()
public virtual void InlineConstraintsSpecifiedInTheRoutePrefixAttribute()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Inline constraints specified in the RoutePrefixAttribute", ((string[])(null)));
-#line 51
+#line 63
this.ScenarioSetup(scenarioInfo);
-#line 53
+#line 65
testRunner.Given("I have registered the routes for the PrefixedInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 54
+#line 66
testRunner.When("I fetch the routes for the PrefixedInlineRouteConstraints controller\'s Index acti" +
"on", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 55
+#line 67
testRunner.Then("the route url is \"Prefixed-Inline-Constraints/{id}/Howdy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 56
+#line 68
testRunner.And("the parameter \"id\" is constrained by an inline AttributeRouting.Web.Constraints.I" +
"ntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 58
+#line 70
testRunner.Given("I have registered the routes for the HttpPrefixedInlineRouteConstraintsController" +
"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 59
+#line 71
testRunner.When("I fetch the routes for the HttpPrefixedInlineRouteConstraints controller\'s Index " +
"action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 60
+#line 72
testRunner.Then("the route url is \"Http-Prefixed-Inline-Constraints/{id}/Howdy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 61
+#line 73
testRunner.And("the parameter \"id\" is constrained by an inline AttributeRouting.Web.Constraints.I" +
"ntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
@@ -187,25 +218,25 @@ public virtual void InlineConstraintsSpecifiedInTheRoutePrefixAttribute()
public virtual void InlineConstraintsSpecifiedInTheRouteAreaAttribute()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Inline constraints specified in the RouteAreaAttribute", ((string[])(null)));
-#line 63
+#line 75
this.ScenarioSetup(scenarioInfo);
-#line 65
+#line 77
testRunner.Given("I have registered the routes for the AreaInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 66
+#line 78
testRunner.When("I fetch the routes for the AreaInlineRouteConstraints controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 67
+#line 79
testRunner.Then("the route url is \"Area-Inline-Constraints/{id}/Howdy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 68
+#line 80
testRunner.And("the parameter \"id\" is constrained by an inline AttributeRouting.Web.Constraints.I" +
"ntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 70
+#line 82
testRunner.Given("I have registered the routes for the HttpAreaInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 71
+#line 83
testRunner.When("I fetch the routes for the HttpAreaInlineRouteConstraints controller\'s Index acti" +
"on", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 72
+#line 84
testRunner.Then("the route url is \"Http-Area-Inline-Constraints/{id}/Howdy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 73
+#line 85
testRunner.And("the parameter \"id\" is constrained by an inline AttributeRouting.Web.Constraints.I" +
"ntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
@@ -262,22 +293,24 @@ public virtual void InlineConstraintsSpecifiedInTheRouteAreaAttribute()
[NUnit.Framework.TestCaseAttribute("Enum/taupe", "Enum", "is not", null)]
[NUnit.Framework.TestCaseAttribute("WithOptional", "WithOptional", "is", null)]
[NUnit.Framework.TestCaseAttribute("WithDefault", "WithDefault", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Querystring?x=123", "Querystring", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Querystring?x=abc", "Querystring", "is not", null)]
public virtual void MatchingInlineRouteConstraints(string url, string action, string condition, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Matching inline route constraints", exampleTags);
-#line 75
+#line 87
this.ScenarioSetup(scenarioInfo);
-#line 77
+#line 89
testRunner.Given("I have registered the routes for the InlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 78
+#line 90
testRunner.When(string.Format("a request for \"Inline-Constraints/{0}\" is made", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 79
+#line 91
testRunner.Then(string.Format("the {0} action {1} matched", action, condition), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 81
+#line 93
testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 82
+#line 94
testRunner.When(string.Format("a request for \"Http-Inline-Constraints/{0}\" is made", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 83
+#line 95
testRunner.Then(string.Format("the {0} action {1} matched", action, condition), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
diff --git a/src/AttributeRouting.Specs/Steps/RouteConstraintSteps.cs b/src/AttributeRouting.Specs/Steps/RouteConstraintSteps.cs
index 915867d..5d4d239 100644
--- a/src/AttributeRouting.Specs/Steps/RouteConstraintSteps.cs
+++ b/src/AttributeRouting.Specs/Steps/RouteConstraintSteps.cs
@@ -37,12 +37,20 @@ public void ThenTheParameterIsConstrainedByAnInline(string key, string type)
Assert.That(constraint, Is.Not.Null);
+ // If this is a querystring route constraint wrapper, then unwrap it.
+ var queryStringConstraint = constraint as IQueryStringRouteConstraintWrapper;
+ if (queryStringConstraint != null)
+ constraint = queryStringConstraint.Constraint;
+
var compoundRouteConstraint = constraint as ICompoundRouteConstraintWrapper;
if (compoundRouteConstraint != null)
- Assert.That(compoundRouteConstraint.Constraints.Any(c => c.GetType().FullName == type),
- Is.True);
+ {
+ Assert.That(compoundRouteConstraint.Constraints.Any(c => c.GetType().FullName == type), Is.True);
+ }
else
+ {
Assert.That(constraint.GetType().FullName, Is.EqualTo(type));
+ }
}
}
diff --git a/src/AttributeRouting.Specs/Steps/SharedSteps.cs b/src/AttributeRouting.Specs/Steps/SharedSteps.cs
index 6f9e242..beb65a1 100644
--- a/src/AttributeRouting.Specs/Steps/SharedSteps.cs
+++ b/src/AttributeRouting.Specs/Steps/SharedSteps.cs
@@ -3,6 +3,7 @@
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
+using System.Web;
using System.Web.Http;
using System.Web.Routing;
using AttributeRouting.Helpers;
@@ -163,11 +164,17 @@ public void WhenAMethodRequestForUrlIsMade(string method, string url)
{
var desiredMethod = (method.HasValue() ? method : "GET").Trim().ToUpperInvariant();
var requestMethod = (desiredMethod.ValueEquals("GET") ? "GET" : "POST").ToUpperInvariant();
+ var pathAndQuery = url.SplitAndTrim("?");
var httpContextMock = MockBuilder.BuildMockHttpContext(r =>
{
- r.SetupGet(x => x.AppRelativeCurrentExecutionFilePath).Returns("~/" + Regex.Replace(url, @"[{}]", ""));
r.SetupGet(x => x.HttpMethod).Returns(requestMethod);
+ r.SetupGet(x => x.AppRelativeCurrentExecutionFilePath).Returns("~/" + Regex.Replace(pathAndQuery[0], @"[{}]", ""));
+
+ if (pathAndQuery.Length > 1)
+ {
+ r.SetupGet(x => x.QueryString).Returns(HttpUtility.ParseQueryString(pathAndQuery[1]));
+ }
if (desiredMethod != requestMethod)
{
diff --git a/src/AttributeRouting.Specs/Subjects/Http/HttpInlineRouteConstraintsControllers.cs b/src/AttributeRouting.Specs/Subjects/Http/HttpInlineRouteConstraintsControllers.cs
index 2d10b6e..bdd4d18 100644
--- a/src/AttributeRouting.Specs/Subjects/Http/HttpInlineRouteConstraintsControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/Http/HttpInlineRouteConstraintsControllers.cs
@@ -27,6 +27,12 @@ public string Index()
[RoutePrefix("Http-Inline-Constraints")]
public class HttpInlineRouteConstraintsController : ApiController
{
+ [GET("Querystring?x={x:int}")]
+ public string Querystring(int x)
+ {
+ return "";
+ }
+
[GET("Alpha/{x:alpha}")]
public string Alpha(string x)
{
diff --git a/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs b/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs
index 44ee752..7b1df57 100644
--- a/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs
@@ -27,6 +27,12 @@ public string Index()
[RoutePrefix("Inline-Constraints")]
public class InlineRouteConstraintsController : Controller
{
+ [GET("Querystring?x={x:int}")]
+ public string Querystring(int x)
+ {
+ return "";
+ }
+
[GET("Alpha/{x:alpha}")]
public string Alpha(string x)
{
diff --git a/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj b/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj
index c3880ef..277dbbf 100644
--- a/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj
+++ b/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj
@@ -31,6 +31,10 @@
4.0
+
+
+
+
true
@@ -126,6 +130,7 @@
+
diff --git a/src/AttributeRouting.Tests.Web/Controllers/QueryStringConstraintController.cs b/src/AttributeRouting.Tests.Web/Controllers/QueryStringConstraintController.cs
new file mode 100644
index 0000000..9c54c5d
--- /dev/null
+++ b/src/AttributeRouting.Tests.Web/Controllers/QueryStringConstraintController.cs
@@ -0,0 +1,20 @@
+using System.Web.Mvc;
+using AttributeRouting.Web.Mvc;
+
+namespace AttributeRouting.Tests.Web.Controllers
+{
+ public class QueryStringConstraintController : Controller
+ {
+ [GET("Books?author={author:int}")]
+ public string GetBooksByAuthor(string author)
+ {
+ return "author: " + author;
+ }
+
+ [GET("Books?isbn={isbn:alpha}")]
+ public string GetBooksByISBN(string isbn)
+ {
+ return "isbn: " + isbn;
+ }
+ }
+}
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
index df49312..2133c36 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
@@ -58,5 +58,10 @@ public IOptionalRouteConstraintWrapper CreateOptionalRouteConstraint(object cons
{
return new OptionalRouteConstraintWrapper((IHttpRouteConstraint)constraint);
}
+
+ public IQueryStringRouteConstraintWrapper CreateQueryStringRouteConstraint(object constraint)
+ {
+ return new QueryStringRouteConstraintWrapper((IHttpRouteConstraint)constraint);
+ }
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
index 7b7be85..50f5f98 100644
--- a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
@@ -56,5 +56,10 @@ public IOptionalRouteConstraintWrapper CreateOptionalRouteConstraint(object cons
{
return new OptionalRouteConstraintWrapper((IRouteConstraint)constraint);
}
+
+ public IQueryStringRouteConstraintWrapper CreateQueryStringRouteConstraint(object constraint)
+ {
+ return new QueryStringRouteConstraintWrapper((IRouteConstraint)constraint);
+ }
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj b/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
index 6407148..ec4d572 100644
--- a/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
+++ b/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
@@ -51,6 +51,7 @@
..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll
+
..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll
@@ -72,6 +73,7 @@
+
diff --git a/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraintWrapper.cs b/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraintWrapper.cs
new file mode 100644
index 0000000..a19ae16
--- /dev/null
+++ b/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraintWrapper.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Web;
+using System.Web.Http.Routing;
+using AttributeRouting.Constraints;
+
+namespace AttributeRouting.Web.Http.Constraints
+{
+ public class QueryStringRouteConstraintWrapper : IQueryStringRouteConstraintWrapper, IHttpRouteConstraint
+ {
+ private readonly IHttpRouteConstraint _constraint;
+
+ public QueryStringRouteConstraintWrapper(IHttpRouteConstraint constraint)
+ {
+ _constraint = constraint;
+ }
+
+ public object Constraint
+ {
+ get { return _constraint; }
+ }
+
+ public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection)
+ {
+ // If the query param does not exist, then fail.
+ var queryString = HttpUtility.ParseQueryString(request.RequestUri.Query);
+ if (!queryString.AllKeys.Contains(parameterName))
+ return false;
+
+ // Process the constraint.
+ var queryRouteValues = new Dictionary
+ {
+ { parameterName, queryString[parameterName] }
+ };
+
+ return _constraint.Match(request, route, parameterName, queryRouteValues, routeDirection);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
index fc0180d..72b09f1 100644
--- a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
@@ -56,5 +56,10 @@ public IOptionalRouteConstraintWrapper CreateOptionalRouteConstraint(object cons
{
return new OptionalRouteConstraintWrapper((IRouteConstraint)constraint);
}
+
+ public IQueryStringRouteConstraintWrapper CreateQueryStringRouteConstraint(object constraint)
+ {
+ return new QueryStringRouteConstraintWrapper((IRouteConstraint)constraint);
+ }
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web/AttributeRouting.Web.csproj b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
index b8c46b7..ebc0c46 100644
--- a/src/AttributeRouting.Web/AttributeRouting.Web.csproj
+++ b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
@@ -61,6 +61,7 @@
+
diff --git a/src/AttributeRouting.Web/Constraints/QueryStringRouteConstraintWrapper.cs b/src/AttributeRouting.Web/Constraints/QueryStringRouteConstraintWrapper.cs
new file mode 100644
index 0000000..b0ca05b
--- /dev/null
+++ b/src/AttributeRouting.Web/Constraints/QueryStringRouteConstraintWrapper.cs
@@ -0,0 +1,38 @@
+using System.Web;
+using System.Web.Routing;
+using AttributeRouting.Constraints;
+using System.Linq;
+
+namespace AttributeRouting.Web.Constraints
+{
+ public class QueryStringRouteConstraintWrapper : IQueryStringRouteConstraintWrapper, IRouteConstraint
+ {
+ private readonly IRouteConstraint _constraint;
+
+ public QueryStringRouteConstraintWrapper(IRouteConstraint constraint)
+ {
+ _constraint = constraint;
+ }
+
+ public object Constraint
+ {
+ get { return _constraint; }
+ }
+
+ public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
+ {
+ // If the query param does not exist, then fail.
+ var queryString = httpContext.Request.QueryString;
+ if (!queryString.AllKeys.Contains(parameterName))
+ return false;
+
+ // Process the constraint.
+ var queryRouteValues = new RouteValueDictionary
+ {
+ { parameterName, queryString[parameterName] }
+ };
+
+ return _constraint.Match(httpContext, route, parameterName, queryRouteValues, routeDirection);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting/AttributeRouting.csproj b/src/AttributeRouting/AttributeRouting.csproj
index dd141c7..f7d136e 100644
--- a/src/AttributeRouting/AttributeRouting.csproj
+++ b/src/AttributeRouting/AttributeRouting.csproj
@@ -63,6 +63,7 @@
+
diff --git a/src/AttributeRouting/Constraints/IQueryStringRouteConstraintWrapper.cs b/src/AttributeRouting/Constraints/IQueryStringRouteConstraintWrapper.cs
new file mode 100644
index 0000000..34c63db
--- /dev/null
+++ b/src/AttributeRouting/Constraints/IQueryStringRouteConstraintWrapper.cs
@@ -0,0 +1,21 @@
+using AttributeRouting.Framework.Factories;
+
+namespace AttributeRouting.Constraints
+{
+ ///
+ /// Abstraction used by
+ /// when handling inline route constraints contained in query strings.
+ ///
+ ///
+ /// Due to
+ /// System.Web.Routing.IRouteConstraint (used in web-hosted scenarios) and
+ /// System.Web.Http.Routing.IHttpRouteConstraint (used in self-hosted scenarios).
+ ///
+ public interface IQueryStringRouteConstraintWrapper
+ {
+ ///
+ /// The constraint used in the query string.
+ ///
+ object Constraint { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs b/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs
index 385e904..708c0c4 100644
--- a/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs
+++ b/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs
@@ -42,5 +42,11 @@ public interface IRouteConstraintFactory
///
/// The constraint
IOptionalRouteConstraintWrapper CreateOptionalRouteConstraint(object constraint);
+
+ ///
+ /// Creates an query string route constraint wrapper to allow constraints in the query string.
+ ///
+ /// The constraint
+ IQueryStringRouteConstraintWrapper CreateQueryStringRouteConstraint(object constraint);
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 66e2ff7..7a7c5b4 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -107,9 +107,10 @@ private string CreateRouteUrl(RouteSpecification routeSpec)
private string CreateRouteUrl(string routeUrl, string routePrefix, string areaUrl, bool isAbsoluteUrl, bool? useLowercaseRoute)
{
var tokenizedUrl = BuildTokenizedUrl(routeUrl, routePrefix, areaUrl, isAbsoluteUrl);
- var detokenizedUrl = DetokenizeUrl(tokenizedUrl);
+ var tokenizedPath = RemoveQueryString(tokenizedUrl);
+ var detokenizedPath = DetokenizeUrl(tokenizedPath);
- var urlParameterNames = GetUrlParameterContents(detokenizedUrl).ToList();
+ var urlParameterNames = GetUrlParameterContents(detokenizedPath).ToList();
// {controller} and {action} tokens are not valid
if (urlParameterNames.Any(n => n.ValueEquals("controller")))
@@ -122,7 +123,7 @@ private string CreateRouteUrl(string routeUrl, string routePrefix, string areaUr
throw new AttributeRoutingException(
"{area} url parameters are not allowed. Specify the area name by using the RouteAreaAttribute.");
- var urlBuilder = new StringBuilder(detokenizedUrl);
+ var urlBuilder = new StringBuilder(detokenizedPath);
// If we are lowercasing routes, then lowercase everything but the route params
var lower = useLowercaseRoute.HasValue ? useLowercaseRoute.Value : _configuration.UseLowercaseRoutes;
@@ -201,20 +202,28 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
var tokenizedUrl = BuildTokenizedUrl(routeSpec.RouteUrl, routeSpec.RoutePrefixUrl, routeSpec.AreaUrl, routeSpec.IsAbsoluteUrl);
var urlParameters = GetUrlParameterContents(tokenizedUrl).ToList();
+ // Need to keep track of query params.
+ // Can do this by detokenizing URL (which strips query),
+ // and then taking all the URL parameters except those from the path part of the URL.
+ var pathOnlyUrl = RemoveQueryString(tokenizedUrl);
+ var pathOnlyUrlParameters = GetUrlParameterContents(pathOnlyUrl);
+ var queryStringParameters = urlParameters.Except(pathOnlyUrlParameters).ToList();
+
// Inline constraints
var constraintFactory = _configuration.RouteConstraintFactory;
foreach (var parameter in urlParameters.Where(p => p.Contains(":")))
{
- // Keep track of whether this param is optional,
+ // Keep track of whether this param is optional or in the querystring,
// because we wrap the final constraint if so.
var parameterIsOptional = parameter.EndsWith("?");
+ var parameterIsInQueryString = queryStringParameters.Contains(parameter);
// Strip off everything related to defaults.
var cleanParameter = parameter.TrimEnd('?').Split('=').FirstOrDefault();
var sections = cleanParameter.SplitAndTrim(":");
var parameterName = sections.First();
-
+
// Do not override default or legacy inline constraints
if (constraints.ContainsKey(parameterName))
continue;
@@ -256,18 +265,34 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
inlineConstraints.Add(constraint);
}
- // Apply the constraint to the parameter.
- // Wrap multiple constraints in a compound constraint wrapper.
- // Wrap constraints for optional params in an optional constraint wrapper.
- var finalConstraint = (inlineConstraints.Count == 1)
- ? inlineConstraints.Single()
- : constraintFactory.CreateCompoundRouteConstraint(inlineConstraints.ToArray());
+ // Apply the constraint to the parameter, and wrap constraints in the following priority:
+ object finalConstraint;
- if (parameterIsOptional)
- constraints.Add(parameterName, constraintFactory.CreateOptionalRouteConstraint(finalConstraint));
+ // 1. If more than one constraint, wrap in a compound constraint.
+ if (inlineConstraints.Count > 1)
+ {
+ finalConstraint = constraintFactory.CreateCompoundRouteConstraint(inlineConstraints.ToArray());
+ }
else
- constraints.Add(parameterName, finalConstraint);
- }
+ {
+ finalConstraint = inlineConstraints.Single();
+ }
+
+ // 2. If the constraint is in the querystring, wrap in a query string constraint.
+ if (parameterIsInQueryString)
+ {
+ finalConstraint = constraintFactory.CreateQueryStringRouteConstraint(finalConstraint);
+ }
+
+ // 3. If the constraint is optional, wrap in an optional constraint.
+ if (parameterIsOptional)
+ {
+ finalConstraint = constraintFactory.CreateOptionalRouteConstraint(finalConstraint);
+ }
+
+ constraints.Add(parameterName, finalConstraint);
+
+ } // ... go to next parameter
// Globally configured constraints
var detokenizedPrefixedUrl = DetokenizeUrl(tokenizedUrl);
@@ -344,6 +369,29 @@ private static string DetokenizeUrl(string url)
return Regex.Replace(url, String.Join("|", patterns), "");
}
+ private static string RemoveQueryString(string url)
+ {
+ // Must honor ? in regex expressions and as used to specify optional params,
+ // So run through the url chars and fast forward when inside a url param (eg: {...})
+ for (int i = 0, length = url.Length; i < length; i++)
+ {
+ var c = url[i];
+ if (c == '?')
+ {
+ return url.Substring(0, i);
+ }
+
+ // Fast-forward past url param contents
+ if (c == '{')
+ {
+ while (url[i] != '}' && i < length)
+ i++;
+ }
+ }
+
+ return url;
+ }
+
private IEnumerable CreateRouteTranslations(RouteSpecification routeSpec)
{
// If no translation provider, then get out of here.
From 37e415a2ac8d37d34d01f97c9843b215a16eb5d1 Mon Sep 17 00:00:00 2001
From: ourben
Date: Fri, 30 Nov 2012 17:32:25 +0000
Subject: [PATCH 03/97] Update src/AttributeRouting/Framework/RouteBuilder.cs
Fixed a little typo there that was causing dEaTh!
---
src/AttributeRouting/Framework/RouteBuilder.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 7d36919..c8c8113 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -422,7 +422,7 @@ private static IEnumerable GetUrlParameterContents(string url)
{
// If we find an inner open curly (due to inner regex patterns),
// then fast-forward beyond it.
- i = urlSegment.IndexOf('}', iOpenCurly);
+ i = urlSegment.IndexOf('}', i);
}
i++;
From 254dceb5ef929b8f248b6016d4a08efd29c98ab8 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Mon, 3 Dec 2012 07:33:19 -0700
Subject: [PATCH 04/97] #152 - added support for specifying that query param
simply exists, without constraining by anything else.
---
.../Features/RouteConstraints.feature | 6 +-
.../Features/RouteConstraints.feature.cs | 90 ++++++++++---------
.../Steps/RouteConstraintSteps.cs | 4 +-
.../HttpInlineRouteConstraintsControllers.cs | 4 +-
.../InlineRouteConstraintsControllers.cs | 4 +-
.../QueryStringConstraintController.cs | 6 +-
.../QueryStringRouteConstraintWrapper.cs | 3 +-
.../QueryStringRouteConstraintWrapper.cs | 3 +-
.../Framework/RouteBuilder.cs | 8 +-
9 files changed, 74 insertions(+), 54 deletions(-)
diff --git a/src/AttributeRouting.Specs/Features/RouteConstraints.feature b/src/AttributeRouting.Specs/Features/RouteConstraints.feature
index c8db6d5..ed0f9cb 100644
--- a/src/AttributeRouting.Specs/Features/RouteConstraints.feature
+++ b/src/AttributeRouting.Specs/Features/RouteConstraints.feature
@@ -40,11 +40,13 @@ Scenario: Inline constraints in the querystring
When I fetch the routes for the InlineRouteConstraints controller's Querystring action
Then the route url is "Inline-Constraints/Querystring"
And the parameter "x" is constrained by an inline AttributeRouting.Web.Constraints.IntRouteConstraint
+ And the parameter "y" is constrained by an inline AttributeRouting.Web.Constraints.QueryStringRouteConstraintWrapper
# Web API
Given I have registered the routes for the HttpInlineRouteConstraintsController
When I fetch the routes for the HttpInlineRouteConstraints controller's Querystring action
Then the route url is "Http-Inline-Constraints/Querystring"
And the parameter "x" is constrained by an inline AttributeRouting.Web.Constraints.IntRouteConstraint
+ And the parameter "y" is constrained by an inline AttributeRouting.Web.Constraints.QueryStringRouteConstraintWrapper
Scenario: Multiple inline constraints per url segment
# MVC
@@ -143,5 +145,7 @@ Scenario Outline: Matching inline route constraints
| Enum/taupe | Enum | is not |
| WithOptional | WithOptional | is |
| WithDefault | WithDefault | is |
- | Querystring?x=123 | Querystring | is |
+ | Querystring?x=123&y=hello | Querystring | is |
+ | Querystring?x=abc&y=hello | Querystring | is not |
| Querystring?x=abc | Querystring | is not |
+ | Querystring?y=hello | Querystring | is not |
diff --git a/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs b/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
index ea7a00b..17a890d 100644
--- a/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
@@ -130,16 +130,22 @@ public virtual void InlineConstraintsInTheQuerystring()
#line 42
testRunner.And("the parameter \"x\" is constrained by an inline AttributeRouting.Web.Constraints.In" +
"tRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 44
- testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 43
+ testRunner.And("the parameter \"y\" is constrained by an inline AttributeRouting.Web.Constraints.Qu" +
+ "eryStringRouteConstraintWrapper", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 45
+ testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 46
testRunner.When("I fetch the routes for the HttpInlineRouteConstraints controller\'s Querystring ac" +
"tion", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 46
- testRunner.Then("the route url is \"Http-Inline-Constraints/Querystring\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 47
+ testRunner.Then("the route url is \"Http-Inline-Constraints/Querystring\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 48
testRunner.And("the parameter \"x\" is constrained by an inline AttributeRouting.Web.Constraints.In" +
"tRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 49
+ testRunner.And("the parameter \"y\" is constrained by an inline AttributeRouting.Web.Constraints.Qu" +
+ "eryStringRouteConstraintWrapper", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -149,32 +155,32 @@ public virtual void InlineConstraintsInTheQuerystring()
public virtual void MultipleInlineConstraintsPerUrlSegment()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Multiple inline constraints per url segment", ((string[])(null)));
-#line 49
-this.ScenarioSetup(scenarioInfo);
#line 51
+this.ScenarioSetup(scenarioInfo);
+#line 53
testRunner.Given("I have registered the routes for the InlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 52
+#line 54
testRunner.When("I fetch the routes for the InlineRouteConstraints controller\'s MultipleWithinUrlS" +
"egment action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 53
+#line 55
testRunner.Then("the route url is \"Inline-Constraints/avatar/{width}x{height}/{image}\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 54
+#line 56
testRunner.And("the parameter \"width\" is constrained by an inline AttributeRouting.Web.Constraint" +
"s.IntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 55
+#line 57
testRunner.And("the parameter \"height\" is constrained by an inline AttributeRouting.Web.Constrain" +
"ts.IntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 57
+#line 59
testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 58
+#line 60
testRunner.When("I fetch the routes for the HttpInlineRouteConstraints controller\'s MultipleWithin" +
"UrlSegment action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 59
+#line 61
testRunner.Then("the route url is \"Http-Inline-Constraints/avatar/{width}x{height}/{image}\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 60
+#line 62
testRunner.And("the parameter \"width\" is constrained by an inline AttributeRouting.Web.Constraint" +
"s.IntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 61
+#line 63
testRunner.And("the parameter \"height\" is constrained by an inline AttributeRouting.Web.Constrain" +
"ts.IntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
@@ -186,27 +192,27 @@ public virtual void MultipleInlineConstraintsPerUrlSegment()
public virtual void InlineConstraintsSpecifiedInTheRoutePrefixAttribute()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Inline constraints specified in the RoutePrefixAttribute", ((string[])(null)));
-#line 63
-this.ScenarioSetup(scenarioInfo);
#line 65
+this.ScenarioSetup(scenarioInfo);
+#line 67
testRunner.Given("I have registered the routes for the PrefixedInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 66
+#line 68
testRunner.When("I fetch the routes for the PrefixedInlineRouteConstraints controller\'s Index acti" +
"on", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 67
+#line 69
testRunner.Then("the route url is \"Prefixed-Inline-Constraints/{id}/Howdy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 68
+#line 70
testRunner.And("the parameter \"id\" is constrained by an inline AttributeRouting.Web.Constraints.I" +
"ntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 70
+#line 72
testRunner.Given("I have registered the routes for the HttpPrefixedInlineRouteConstraintsController" +
"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 71
+#line 73
testRunner.When("I fetch the routes for the HttpPrefixedInlineRouteConstraints controller\'s Index " +
"action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 72
+#line 74
testRunner.Then("the route url is \"Http-Prefixed-Inline-Constraints/{id}/Howdy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 73
+#line 75
testRunner.And("the parameter \"id\" is constrained by an inline AttributeRouting.Web.Constraints.I" +
"ntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
@@ -218,25 +224,25 @@ public virtual void InlineConstraintsSpecifiedInTheRoutePrefixAttribute()
public virtual void InlineConstraintsSpecifiedInTheRouteAreaAttribute()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Inline constraints specified in the RouteAreaAttribute", ((string[])(null)));
-#line 75
-this.ScenarioSetup(scenarioInfo);
#line 77
+this.ScenarioSetup(scenarioInfo);
+#line 79
testRunner.Given("I have registered the routes for the AreaInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 78
+#line 80
testRunner.When("I fetch the routes for the AreaInlineRouteConstraints controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 79
+#line 81
testRunner.Then("the route url is \"Area-Inline-Constraints/{id}/Howdy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 80
+#line 82
testRunner.And("the parameter \"id\" is constrained by an inline AttributeRouting.Web.Constraints.I" +
"ntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 82
+#line 84
testRunner.Given("I have registered the routes for the HttpAreaInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 83
+#line 85
testRunner.When("I fetch the routes for the HttpAreaInlineRouteConstraints controller\'s Index acti" +
"on", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 84
+#line 86
testRunner.Then("the route url is \"Http-Area-Inline-Constraints/{id}/Howdy\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 85
+#line 87
testRunner.And("the parameter \"id\" is constrained by an inline AttributeRouting.Web.Constraints.I" +
"ntRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
@@ -293,24 +299,26 @@ public virtual void InlineConstraintsSpecifiedInTheRouteAreaAttribute()
[NUnit.Framework.TestCaseAttribute("Enum/taupe", "Enum", "is not", null)]
[NUnit.Framework.TestCaseAttribute("WithOptional", "WithOptional", "is", null)]
[NUnit.Framework.TestCaseAttribute("WithDefault", "WithDefault", "is", null)]
- [NUnit.Framework.TestCaseAttribute("Querystring?x=123", "Querystring", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Querystring?x=123&y=hello", "Querystring", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Querystring?x=abc&y=hello", "Querystring", "is not", null)]
[NUnit.Framework.TestCaseAttribute("Querystring?x=abc", "Querystring", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Querystring?y=hello", "Querystring", "is not", null)]
public virtual void MatchingInlineRouteConstraints(string url, string action, string condition, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Matching inline route constraints", exampleTags);
-#line 87
-this.ScenarioSetup(scenarioInfo);
#line 89
+this.ScenarioSetup(scenarioInfo);
+#line 91
testRunner.Given("I have registered the routes for the InlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 90
+#line 92
testRunner.When(string.Format("a request for \"Inline-Constraints/{0}\" is made", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 91
- testRunner.Then(string.Format("the {0} action {1} matched", action, condition), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 93
+ testRunner.Then(string.Format("the {0} action {1} matched", action, condition), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 95
testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 94
+#line 96
testRunner.When(string.Format("a request for \"Http-Inline-Constraints/{0}\" is made", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 95
+#line 97
testRunner.Then(string.Format("the {0} action {1} matched", action, condition), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
diff --git a/src/AttributeRouting.Specs/Steps/RouteConstraintSteps.cs b/src/AttributeRouting.Specs/Steps/RouteConstraintSteps.cs
index 5d4d239..47e017d 100644
--- a/src/AttributeRouting.Specs/Steps/RouteConstraintSteps.cs
+++ b/src/AttributeRouting.Specs/Steps/RouteConstraintSteps.cs
@@ -39,8 +39,10 @@ public void ThenTheParameterIsConstrainedByAnInline(string key, string type)
// If this is a querystring route constraint wrapper, then unwrap it.
var queryStringConstraint = constraint as IQueryStringRouteConstraintWrapper;
- if (queryStringConstraint != null)
+ if (queryStringConstraint != null && queryStringConstraint.Constraint != null)
+ {
constraint = queryStringConstraint.Constraint;
+ }
var compoundRouteConstraint = constraint as ICompoundRouteConstraintWrapper;
if (compoundRouteConstraint != null)
diff --git a/src/AttributeRouting.Specs/Subjects/Http/HttpInlineRouteConstraintsControllers.cs b/src/AttributeRouting.Specs/Subjects/Http/HttpInlineRouteConstraintsControllers.cs
index bdd4d18..0664ee1 100644
--- a/src/AttributeRouting.Specs/Subjects/Http/HttpInlineRouteConstraintsControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/Http/HttpInlineRouteConstraintsControllers.cs
@@ -27,8 +27,8 @@ public string Index()
[RoutePrefix("Http-Inline-Constraints")]
public class HttpInlineRouteConstraintsController : ApiController
{
- [GET("Querystring?x={x:int}")]
- public string Querystring(int x)
+ [GET("Querystring?{x:int}&{y}")]
+ public string Querystring(int x, string y)
{
return "";
}
diff --git a/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs b/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs
index 7b1df57..125dffe 100644
--- a/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs
@@ -27,8 +27,8 @@ public string Index()
[RoutePrefix("Inline-Constraints")]
public class InlineRouteConstraintsController : Controller
{
- [GET("Querystring?x={x:int}")]
- public string Querystring(int x)
+ [GET("Querystring?{x:int}&{y}")]
+ public string Querystring(int x, string y)
{
return "";
}
diff --git a/src/AttributeRouting.Tests.Web/Controllers/QueryStringConstraintController.cs b/src/AttributeRouting.Tests.Web/Controllers/QueryStringConstraintController.cs
index 9c54c5d..03e4976 100644
--- a/src/AttributeRouting.Tests.Web/Controllers/QueryStringConstraintController.cs
+++ b/src/AttributeRouting.Tests.Web/Controllers/QueryStringConstraintController.cs
@@ -5,13 +5,13 @@ namespace AttributeRouting.Tests.Web.Controllers
{
public class QueryStringConstraintController : Controller
{
- [GET("Books?author={author:int}")]
- public string GetBooksByAuthor(string author)
+ [GET("Books?{author}")]
+ public string GetBooksByAuthor(string author, string damage)
{
return "author: " + author;
}
- [GET("Books?isbn={isbn:alpha}")]
+ [GET("Books?{isbn}")]
public string GetBooksByISBN(string isbn)
{
return "isbn: " + isbn;
diff --git a/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraintWrapper.cs b/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraintWrapper.cs
index a19ae16..bf9ff08 100644
--- a/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraintWrapper.cs
+++ b/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraintWrapper.cs
@@ -34,7 +34,8 @@ public bool Match(HttpRequestMessage request, IHttpRoute route, string parameter
{ parameterName, queryString[parameterName] }
};
- return _constraint.Match(request, route, parameterName, queryRouteValues, routeDirection);
+ return _constraint == null // ie: Simply ensure that the query param exists.
+ || _constraint.Match(request, route, parameterName, queryRouteValues, routeDirection);
}
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web/Constraints/QueryStringRouteConstraintWrapper.cs b/src/AttributeRouting.Web/Constraints/QueryStringRouteConstraintWrapper.cs
index b0ca05b..4004d84 100644
--- a/src/AttributeRouting.Web/Constraints/QueryStringRouteConstraintWrapper.cs
+++ b/src/AttributeRouting.Web/Constraints/QueryStringRouteConstraintWrapper.cs
@@ -32,7 +32,8 @@ public bool Match(HttpContextBase httpContext, Route route, string parameterName
{ parameterName, queryString[parameterName] }
};
- return _constraint.Match(httpContext, route, parameterName, queryRouteValues, routeDirection);
+ return _constraint == null // ie: Simply ensure that the query param exists.
+ || _constraint.Match(httpContext, route, parameterName, queryRouteValues, routeDirection);
}
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 7a7c5b4..aa8b72e 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -211,13 +211,17 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
// Inline constraints
var constraintFactory = _configuration.RouteConstraintFactory;
- foreach (var parameter in urlParameters.Where(p => p.Contains(":")))
+ foreach (var parameter in urlParameters)
{
// Keep track of whether this param is optional or in the querystring,
// because we wrap the final constraint if so.
var parameterIsOptional = parameter.EndsWith("?");
var parameterIsInQueryString = queryStringParameters.Contains(parameter);
+ // If this is a path parameter and doesn't have a constraint, then skip it.
+ if (!parameterIsInQueryString && !parameter.Contains(":"))
+ continue;
+
// Strip off everything related to defaults.
var cleanParameter = parameter.TrimEnd('?').Split('=').FirstOrDefault();
@@ -275,7 +279,7 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
}
else
{
- finalConstraint = inlineConstraints.Single();
+ finalConstraint = inlineConstraints.FirstOrDefault();
}
// 2. If the constraint is in the querystring, wrap in a query string constraint.
From a617590df2c1f5320b1a7856cb0afd2a70d19583 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Mon, 3 Dec 2012 08:10:30 -0700
Subject: [PATCH 05/97] #154 - added support for querystring constraint
description in routes.axd.
---
.../QueryStringConstraintController.cs | 2 +-
.../Logging/AttributeRouteInfo.cs | 29 ++++++++++++-------
2 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/src/AttributeRouting.Tests.Web/Controllers/QueryStringConstraintController.cs b/src/AttributeRouting.Tests.Web/Controllers/QueryStringConstraintController.cs
index 03e4976..e44d2f6 100644
--- a/src/AttributeRouting.Tests.Web/Controllers/QueryStringConstraintController.cs
+++ b/src/AttributeRouting.Tests.Web/Controllers/QueryStringConstraintController.cs
@@ -11,7 +11,7 @@ public string GetBooksByAuthor(string author, string damage)
return "author: " + author;
}
- [GET("Books?{isbn}")]
+ [GET("Books?{isbn}&{x:int}")]
public string GetBooksByISBN(string isbn)
{
return "isbn: " + isbn;
diff --git a/src/AttributeRouting/Logging/AttributeRouteInfo.cs b/src/AttributeRouting/Logging/AttributeRouteInfo.cs
index 25d534e..2a1e0b4 100644
--- a/src/AttributeRouting/Logging/AttributeRouteInfo.cs
+++ b/src/AttributeRouting/Logging/AttributeRouteInfo.cs
@@ -62,13 +62,12 @@ public static AttributeRouteInfo GetRouteInfo(string url,
else
{
var constraintValue = constraint.Value;
- var constraintType = constraint.Value.GetType();
- string constraintAsString;
+ var constraintDescriptions = new List();
// Simple string regex constraint - from ASP.NET routing features
if (constraintValue is string)
{
- constraintAsString = constraintValue.ToString();
+ constraintDescriptions.Add(constraintValue.ToString());
}
else
{
@@ -77,24 +76,30 @@ public static AttributeRouteInfo GetRouteInfo(string url,
if (optionalConstraint != null)
{
constraintValue = optionalConstraint.Constraint;
- constraintType = optionalConstraint.Constraint.GetType();
+ }
+
+ // QueryString constraint - unwrap it and continue
+ var queryStringConstraint = constraintValue as IQueryStringRouteConstraintWrapper;
+ if (queryStringConstraint != null)
+ {
+ constraintValue = queryStringConstraint.Constraint;
+ constraintDescriptions.Add("QueryStringRouteConstraint");
}
// Compound constraint - join type names of the inner constraints
var compoundConstraint = constraintValue as ICompoundRouteConstraintWrapper;
if (compoundConstraint != null)
{
- var innerConstraintTypeNames = compoundConstraint.Constraints.Select(x => x.GetType().Name);
- constraintAsString = String.Join(", ", innerConstraintTypeNames);
+ constraintDescriptions.AddRange(compoundConstraint.Constraints.Select(c => c.GetType().Name));
}
- else
+ else if (constraintValue != null)
{
// Single constraint type
- constraintAsString = constraintType.Name;
+ constraintDescriptions.Add(constraintValue.GetType().Name);
}
}
- item.Constraints.Add(constraint.Key, constraintAsString);
+ item.Constraints.Add(constraint.Key, String.Join(", ", constraintDescriptions));
}
}
}
@@ -107,9 +112,13 @@ public static AttributeRouteInfo GetRouteInfo(string url,
foreach (var token in dataTokens)
{
if (token.Key.ValueEquals("namespaces"))
- item.DataTokens.Add(token.Key, ((string[])token.Value).Aggregate((n1, n2) => n1 + ", " + n2));
+ {
+ item.DataTokens.Add(token.Key, ((string[]) token.Value).Aggregate((n1, n2) => n1 + ", " + n2));
+ }
else
+ {
item.DataTokens.Add(token.Key, token.Value.ToString());
+ }
}
}
From 28fa40e30622751277f84cffb51f9545630f19d2 Mon Sep 17 00:00:00 2001
From: waynebrantley
Date: Mon, 3 Dec 2012 13:10:12 -0500
Subject: [PATCH 06/97] Add compiled regex
---
.../Constraints/RegexRouteConstraintBase.cs | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/AttributeRouting/Constraints/RegexRouteConstraintBase.cs b/src/AttributeRouting/Constraints/RegexRouteConstraintBase.cs
index 58a069c..2b4334e 100644
--- a/src/AttributeRouting/Constraints/RegexRouteConstraintBase.cs
+++ b/src/AttributeRouting/Constraints/RegexRouteConstraintBase.cs
@@ -16,7 +16,9 @@ protected RegexRouteConstraintBase(string pattern)
protected RegexRouteConstraintBase(string pattern, RegexOptions options)
{
Pattern = pattern;
- Options = options;
+ // shouldn't these be included in the derrived classes by default: RegexOptions.CultureInvariant | RegexOptions.IgnoreCase?
+ Options = options; //no need to tell user that it is 'compiled' option...so do not include in public options
+ CompiledExpression = new Regex(pattern, options | RegexOptions.Compiled);
}
///
@@ -24,6 +26,11 @@ protected RegexRouteConstraintBase(string pattern, RegexOptions options)
///
public string Pattern { get; private set; }
+ ///
+ /// The compiled reg-ex expression.
+ ///
+ private Regex CompiledExpression { get; set; }
+
///
/// Regex options for matching.
///
@@ -36,8 +43,7 @@ public bool IsMatch(string parameterName, IDictionary routeDicti
return true;
var valueAsString = value.ToString();
-
- return Regex.IsMatch(valueAsString, Pattern, Options);
+ return CompiledExpression.IsMatch(valueAsString);
}
}
}
From 1e7e0a4d8f8591fb048b3f294e45e74633942835 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Mon, 3 Dec 2012 21:40:33 -0700
Subject: [PATCH 07/97] #150 - changed IAttributeRouteFactory method to return
multiple routes.
---
.../Factories/AttributeRouteFactory.cs | 15 ++--
.../Factories/AttributeRouteFactory.cs | 15 ++--
.../Factories/AttributeRouteFactory.cs | 15 ++--
.../Factories/IAttributeRouteFactory.cs | 7 +-
.../Framework/RouteBuilder.cs | 78 ++++++++++---------
5 files changed, 62 insertions(+), 68 deletions(-)
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/AttributeRouteFactory.cs b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/AttributeRouteFactory.cs
index ecd5a9b..f3c5fd5 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/AttributeRouteFactory.cs
+++ b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/AttributeRouteFactory.cs
@@ -15,16 +15,13 @@ public AttributeRouteFactory(HttpAttributeRoutingConfiguration configuration)
_configuration = configuration;
}
- public IAttributeRoute CreateAttributeRoute(string url,
- IDictionary defaults,
- IDictionary constraints,
- IDictionary dataTokens)
+ public IEnumerable CreateAttributeRoutes(string url, IDictionary defaults, IDictionary constraints, IDictionary dataTokens)
{
- return new HttpAttributeRoute(url,
- new HttpRouteValueDictionary(defaults),
- new HttpRouteValueDictionary(constraints),
- new HttpRouteValueDictionary(dataTokens),
- _configuration);
+ yield return new HttpAttributeRoute(url,
+ new HttpRouteValueDictionary(defaults),
+ new HttpRouteValueDictionary(constraints),
+ new HttpRouteValueDictionary(dataTokens),
+ _configuration);
}
}
}
diff --git a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/AttributeRouteFactory.cs b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/AttributeRouteFactory.cs
index 4961504..47764cd 100644
--- a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/AttributeRouteFactory.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/AttributeRouteFactory.cs
@@ -15,16 +15,13 @@ public AttributeRouteFactory(HttpWebAttributeRoutingConfiguration configuration)
_configuration = configuration;
}
- public IAttributeRoute CreateAttributeRoute(string url,
- IDictionary defaults,
- IDictionary constraints,
- IDictionary dataTokens)
+ public IEnumerable CreateAttributeRoutes(string url, IDictionary defaults, IDictionary constraints, IDictionary dataTokens)
{
- return new HttpAttributeRoute(url,
- new HttpRouteValueDictionary(defaults),
- new HttpRouteValueDictionary(constraints),
- new HttpRouteValueDictionary(dataTokens),
- _configuration);
+ yield return new HttpAttributeRoute(url,
+ new HttpRouteValueDictionary(defaults),
+ new HttpRouteValueDictionary(constraints),
+ new HttpRouteValueDictionary(dataTokens),
+ _configuration);
}
}
}
diff --git a/src/AttributeRouting.Web.Mvc/Framework/Factories/AttributeRouteFactory.cs b/src/AttributeRouting.Web.Mvc/Framework/Factories/AttributeRouteFactory.cs
index 0b384ec..4d1bb43 100644
--- a/src/AttributeRouting.Web.Mvc/Framework/Factories/AttributeRouteFactory.cs
+++ b/src/AttributeRouting.Web.Mvc/Framework/Factories/AttributeRouteFactory.cs
@@ -14,16 +14,13 @@ public AttributeRouteFactory(AttributeRoutingConfiguration configuration)
_configuration = configuration;
}
- public IAttributeRoute CreateAttributeRoute(string url,
- IDictionary defaults,
- IDictionary constraints,
- IDictionary dataTokens)
+ public IEnumerable CreateAttributeRoutes(string url, IDictionary defaults, IDictionary constraints, IDictionary dataTokens)
{
- return new AttributeRoute(url,
- new RouteValueDictionary(defaults),
- new RouteValueDictionary(constraints),
- new RouteValueDictionary(dataTokens),
- _configuration);
+ yield return new AttributeRoute(url,
+ new RouteValueDictionary(defaults),
+ new RouteValueDictionary(constraints),
+ new RouteValueDictionary(dataTokens),
+ _configuration);
}
}
}
diff --git a/src/AttributeRouting/Framework/Factories/IAttributeRouteFactory.cs b/src/AttributeRouting/Framework/Factories/IAttributeRouteFactory.cs
index dac358b..80ced48 100644
--- a/src/AttributeRouting/Framework/Factories/IAttributeRouteFactory.cs
+++ b/src/AttributeRouting/Framework/Factories/IAttributeRouteFactory.cs
@@ -15,11 +15,8 @@ namespace AttributeRouting.Framework.Factories
public interface IAttributeRouteFactory
{
///
- /// Create a new attribute route that wraps an underlying framework route.
+ /// Create attribute routes from the given metadata.
///
- IAttributeRoute CreateAttributeRoute(string url,
- IDictionary defaults,
- IDictionary constraints,
- IDictionary dataTokens);
+ IEnumerable CreateAttributeRoutes(string url, IDictionary defaults, IDictionary constraints, IDictionary dataTokens);
}
}
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 6fdfa08..aa05499 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -53,37 +53,40 @@ where s.Subdomain.HasValue()
private IEnumerable Build(RouteSpecification routeSpec)
{
- var route = _routeFactory.CreateAttributeRoute(CreateRouteUrl(routeSpec),
- CreateRouteDefaults(routeSpec),
- CreateRouteConstraints(routeSpec),
- CreateRouteDataTokens(routeSpec));
+ var routes = _routeFactory.CreateAttributeRoutes(CreateRouteUrl(routeSpec),
+ CreateRouteDefaults(routeSpec),
+ CreateRouteConstraints(routeSpec),
+ CreateRouteDataTokens(routeSpec));
- var routeName = CreateRouteName(routeSpec);
- if (routeName.HasValue())
+ foreach (var route in routes)
{
- route.RouteName = routeName;
- route.DataTokens.Add("routeName", routeName);
- }
+ var routeName = CreateRouteName(routeSpec);
+ if (routeName.HasValue())
+ {
+ route.RouteName = routeName;
+ route.DataTokens.Add("routeName", routeName);
+ }
- route.Translations = CreateRouteTranslations(routeSpec);
- route.Subdomain = routeSpec.Subdomain;
- route.UseLowercaseRoute = routeSpec.UseLowercaseRoute;
- route.PreserveCaseForUrlParameters = routeSpec.PreserveCaseForUrlParameters;
- route.AppendTrailingSlash = routeSpec.AppendTrailingSlash;
+ route.Translations = CreateRouteTranslations(routeSpec);
+ route.Subdomain = routeSpec.Subdomain;
+ route.UseLowercaseRoute = routeSpec.UseLowercaseRoute;
+ route.PreserveCaseForUrlParameters = routeSpec.PreserveCaseForUrlParameters;
+ route.AppendTrailingSlash = routeSpec.AppendTrailingSlash;
- // Yield the default route first
- yield return route;
+ // Yield the default route first
+ yield return route;
- // Then yield the translations
- if (route.Translations == null)
- yield break;
+ // Then yield the translations
+ if (route.Translations == null)
+ yield break;
- foreach (var translation in route.Translations)
- {
- // Backreference the default route.
- translation.DefaultRouteContainer = route;
+ foreach (var translation in route.Translations)
+ {
+ // Backreference the default route.
+ translation.DefaultRouteContainer = route;
- yield return translation;
+ yield return translation;
+ }
}
}
@@ -433,22 +436,25 @@ private IEnumerable CreateRouteTranslations(RouteSpecification
routeSpec.IsAbsoluteUrl,
routeSpec.UseLowercaseRoute);
- var translatedRoute = _routeFactory.CreateAttributeRoute(routeUrl,
- CreateRouteDefaults(routeSpec),
- CreateRouteConstraints(routeSpec),
- CreateRouteDataTokens(routeSpec));
+ var translatedRoutes = _routeFactory.CreateAttributeRoutes(routeUrl,
+ CreateRouteDefaults(routeSpec),
+ CreateRouteConstraints(routeSpec),
+ CreateRouteDataTokens(routeSpec));
- var routeName = CreateRouteName(routeSpec);
- if (routeName != null)
+ foreach (var translatedRoute in translatedRoutes)
{
- translatedRoute.RouteName = routeName;
- translatedRoute.DataTokens.Add("routeName", routeName);
- }
+ var routeName = CreateRouteName(routeSpec);
+ if (routeName != null)
+ {
+ translatedRoute.RouteName = routeName;
+ translatedRoute.DataTokens.Add("routeName", routeName);
+ }
- translatedRoute.CultureName = cultureName;
- translatedRoute.DataTokens.Add("cultureName", cultureName);
+ translatedRoute.CultureName = cultureName;
+ translatedRoute.DataTokens.Add("cultureName", cultureName);
- yield return translatedRoute;
+ yield return translatedRoute;
+ }
}
}
From e6757686318b67525a6367fe35dc0359294af192 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Mon, 3 Dec 2012 22:20:16 -0700
Subject: [PATCH 08/97] updated readme and assembly info for v3.3
---
README.textile | 9 +++++++++
src/SharedAssemblyInfo.cs | 6 +++---
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/README.textile b/README.textile
index b00ff92..f091f4d 100644
--- a/README.textile
+++ b/README.textile
@@ -4,6 +4,15 @@ h2. Please refer to "attributerouting.net":http://attributerouting.net/ for docu
h3. Changelog
+_3.3_
+
+* #153 - fixed bad parsing of regex route constraint patterns that use a comma.
+* #132 - basic support for constraints in the querystring.
+* #152 - added support for specifying that query param simply exists, without constraining by anything else.
+* #154 - added support for querystring constraint description in routes.axd.
+* #159 - added compiled regex in RegexRouteConstraint to improve performance.
+* #150 - changed IAttributeRouteFactory method to return multiple routes.
+
_3.2_
* #142 - Now accepting inline route constraints in the area url part of routes."
diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs
index c9cc613..8a3e3f9 100644
--- a/src/SharedAssemblyInfo.cs
+++ b/src/SharedAssemblyInfo.cs
@@ -7,7 +7,7 @@
[assembly: AssemblyProduct("AttributeRouting")]
[assembly: AssemblyCopyright("Copyright 2010-2012 Tim McCall")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("3.2")]
-[assembly: AssemblyFileVersion("3.2")]
-[assembly: AssemblyInformationalVersion("3.2")]
+[assembly: AssemblyVersion("3.3")]
+[assembly: AssemblyFileVersion("3.3")]
+[assembly: AssemblyInformationalVersion("3.3")]
[assembly: AssemblyConfiguration("Release")]
From a0152b89e704a421bf992620cf2e56e52295196b Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sat, 8 Dec 2012 13:32:46 -0700
Subject: [PATCH 09/97] #162 - modified design of route convention attribute
and added facility for specifying an area attribute for a controller.
---
.../AttributeRouting.Specs.csproj | 2 +-
.../Features/RouteConventions.feature | 76 +++++-----
.../Features/RouteConventions.feature.cs | 29 +++-
.../Steps/StandardUsageSteps.cs | 9 ++
...ller.cs => RouteConventionsControllers.cs} | 28 ++++
.../DefaultHttpRouteConventionAttribute.cs | 4 +-
.../RestfulRouteConventionAttribute.cs | 142 +++++++++---------
.../Framework/RouteReflector.cs | 34 ++---
.../RouteConventionAttributeBase.cs | 31 +++-
9 files changed, 217 insertions(+), 138 deletions(-)
rename src/AttributeRouting.Specs/Subjects/{RouteConventionsController.cs => RouteConventionsControllers.cs} (75%)
diff --git a/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj b/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
index 49f1048..e583024 100644
--- a/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
+++ b/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
@@ -159,7 +159,7 @@
-
+
diff --git a/src/AttributeRouting.Specs/Features/RouteConventions.feature b/src/AttributeRouting.Specs/Features/RouteConventions.feature
index 803e717..3714479 100644
--- a/src/AttributeRouting.Specs/Features/RouteConventions.feature
+++ b/src/AttributeRouting.Specs/Features/RouteConventions.feature
@@ -9,16 +9,16 @@ Scenario Outline: Generating routes using the RestfulRouteConvention
And the route for is constrained to requests
Examples:
- | action | method | url |
- | Index | GET | RestfulRouteConvention |
- | New | GET | RestfulRouteConvention/New |
- | Create | POST | RestfulRouteConvention |
- | Show | GET | RestfulRouteConvention/{id} |
- | Edit | GET | RestfulRouteConvention/{id}/Edit |
- | Update | PUT | RestfulRouteConvention/{id} |
- | Delete | GET | RestfulRouteConvention/{id}/Delete |
- | Destroy | DELETE | RestfulRouteConvention/{id} |
- | Custom | GET | RestfulRouteConvention/Custom |
+ | action | method | url |
+ | Index | GET | RestfulRouteConvention |
+ | New | GET | RestfulRouteConvention/New |
+ | Create | POST | RestfulRouteConvention |
+ | Show | GET | RestfulRouteConvention/{id} |
+ | Edit | GET | RestfulRouteConvention/{id}/Edit |
+ | Update | PUT | RestfulRouteConvention/{id} |
+ | Delete | GET | RestfulRouteConvention/{id}/Delete |
+ | Destroy | DELETE | RestfulRouteConvention/{id} |
+ | Custom | GET | RestfulRouteConvention/Custom |
Scenario Outline: Generating routes using the RestfulRouteConvention on controllers with a RoutePrefix attribute
Given I have registered the routes for the RestfulRouteConventionPrefixController
@@ -29,15 +29,15 @@ Scenario Outline: Generating routes using the RestfulRouteConvention on controll
And the route for is constrained to requests
Examples:
- | action | method | url |
- | Index | GET | Prefix |
- | New | GET | Prefix/New |
- | Create | POST | Prefix |
- | Show | GET | Prefix/{id} |
- | Edit | GET | Prefix/{id}/Edit |
- | Update | PUT | Prefix/{id} |
- | Delete | GET | Prefix/{id}/Delete |
- | Destroy | DELETE | Prefix/{id} |
+ | action | method | url |
+ | Index | GET | Prefix |
+ | New | GET | Prefix/New |
+ | Create | POST | Prefix |
+ | Show | GET | Prefix/{id} |
+ | Edit | GET | Prefix/{id}/Edit |
+ | Update | PUT | Prefix/{id} |
+ | Delete | GET | Prefix/{id}/Delete |
+ | Destroy | DELETE | Prefix/{id} |
Scenario Outline: Generating routes using the DefaultHttpRouteConvention
Given I have registered the routes for the DefaultHttpRouteConventionController
@@ -48,13 +48,13 @@ Scenario Outline: Generating routes using the DefaultHttpRouteConvention
And the route for is constrained to requests
Examples:
- | action | method | url |
- | GetAll | GET | DefaultHttpRouteConvention |
- | Get | GET | DefaultHttpRouteConvention/{id} |
- | Post | POST | DefaultHttpRouteConvention |
- | Put | PUT | DefaultHttpRouteConvention/{id} |
- | Delete | DELETE | DefaultHttpRouteConvention/{id} |
- | Custom | GET | DefaultHttpRouteConvention/Custom |
+ | action | method | url |
+ | GetAll | GET | DefaultHttpRouteConvention |
+ | Get | GET | DefaultHttpRouteConvention/{id} |
+ | Post | POST | DefaultHttpRouteConvention |
+ | Put | PUT | DefaultHttpRouteConvention/{id} |
+ | Delete | DELETE | DefaultHttpRouteConvention/{id} |
+ | Custom | GET | DefaultHttpRouteConvention/Custom |
Scenario Outline: Generating routes using the DefaultHttpRouteConventionPrefix on controllers with a RoutePrefix attribute
Given I have registered the routes for the DefaultHttpRouteConventionPrefixController
@@ -65,13 +65,13 @@ Scenario Outline: Generating routes using the DefaultHttpRouteConventionPrefix o
And the route for is constrained to requests
Examples:
- | action | method | url |
- | GetAll | GET | Prefix |
- | Get | GET | Prefix/{id} |
- | Post | POST | Prefix |
- | Put | PUT | Prefix/{id} |
- | Delete | DELETE | Prefix/{id} |
- | Custom | GET | Prefix/Custom |
+ | action | method | url |
+ | GetAll | GET | Prefix |
+ | Get | GET | Prefix/{id} |
+ | Post | POST | Prefix |
+ | Put | PUT | Prefix/{id} |
+ | Delete | DELETE | Prefix/{id} |
+ | Custom | GET | Prefix/Custom |
Scenario: Generating routes using the RestfulRouteConvention on actions with an explicit route defined
Given I have registered the routes for the RestfulRouteConventionWithExplicitRouteController
@@ -95,4 +95,12 @@ Scenario: Generating routes using the DefaultHttpRouteConvention on actions with
Given I have registered the routes for the DefaultHttpRouteConventionWithExplicitOrderedRouteController
When I fetch the routes for the DefaultHttpRouteConventionWithExplicitOrderedRoute controller's Get action
Then the 1st route url is "DefaultHttpRouteConventionWithExplicitOrderedRoute/Primary"
- And the 2nd route url is "DefaultHttpRouteConventionWithExplicitOrderedRoute"
\ No newline at end of file
+ And the 2nd route url is "DefaultHttpRouteConventionWithExplicitOrderedRoute"
+
+Scenario: Generating routes using the conventions that define areas on controllers
+ Given I have registered the routes for the AreaRouteConventionController
+ When I fetch the routes for the AreaRouteConvention controller's Index action
+ Then the route url is "Subjects/Index"
+ And the default for "controller" is "AreaRouteConvention"
+ And the default for "action" is "Index"
+ And the route area is "Subjects"
diff --git a/src/AttributeRouting.Specs/Features/RouteConventions.feature.cs b/src/AttributeRouting.Specs/Features/RouteConventions.feature.cs
index f388969..101219c 100644
--- a/src/AttributeRouting.Specs/Features/RouteConventions.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteConventions.feature.cs
@@ -1,9 +1,9 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.9.1.84
+// SpecFlow Version:1.9.0.77
// SpecFlow Generator Version:1.9.0.0
-// Runtime Version:4.0.30319.17929
+// Runtime Version:4.0.30319.18010
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.1.84")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Route Conventions")]
@@ -279,6 +279,29 @@ public virtual void GeneratingRoutesUsingTheDefaultHttpRouteConventionOnActionsW
"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 98
testRunner.And("the 2nd route url is \"DefaultHttpRouteConventionWithExplicitOrderedRoute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Generating routes using the conventions that define areas on controllers")]
+ public virtual void GeneratingRoutesUsingTheConventionsThatDefineAreasOnControllers()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes using the conventions that define areas on controllers", ((string[])(null)));
+#line 100
+this.ScenarioSetup(scenarioInfo);
+#line 101
+ testRunner.Given("I have registered the routes for the AreaRouteConventionController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 102
+ testRunner.When("I fetch the routes for the AreaRouteConvention controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 103
+ testRunner.Then("the route url is \"Subjects/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 104
+ testRunner.And("the default for \"controller\" is \"AreaRouteConvention\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 105
+ testRunner.And("the default for \"action\" is \"Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 106
+ testRunner.And("the route area is \"Subjects\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
index da3ad9d..8b4a54c 100644
--- a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
+++ b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
@@ -32,6 +32,15 @@ public void ThenTheDefaultForIs(string key, object value)
Assert.That(route.Defaults[key], Is.EqualTo(value));
}
+ [Then(@"the route area is ""(.*?)""")]
+ public void ThenTheRouteAreaIs(string area)
+ {
+ var route = ScenarioContext.Current.GetFetchedRoutes().FirstOrDefault();
+
+ Assert.That(route, Is.Not.Null);
+ Assert.That(route.DataTokens["area"], Is.EqualTo(area));
+ }
+
[Then(@"the namespace is ""(.*?)""")]
public void ThenTheNamespaceIs(string ns)
{
diff --git a/src/AttributeRouting.Specs/Subjects/RouteConventionsController.cs b/src/AttributeRouting.Specs/Subjects/RouteConventionsControllers.cs
similarity index 75%
rename from src/AttributeRouting.Specs/Subjects/RouteConventionsController.cs
rename to src/AttributeRouting.Specs/Subjects/RouteConventionsControllers.cs
index c6825e2..b71ffb7 100644
--- a/src/AttributeRouting.Specs/Subjects/RouteConventionsController.cs
+++ b/src/AttributeRouting.Specs/Subjects/RouteConventionsControllers.cs
@@ -1,4 +1,9 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
using System.Web.Mvc;
+using AttributeRouting.Helpers;
using AttributeRouting.Web.Mvc;
namespace AttributeRouting.Specs.Subjects
@@ -118,4 +123,27 @@ public ActionResult Index()
return Content("");
}
}
+
+ [AreaRouteConvention]
+ public class AreaRouteConventionController : Controller
+ {
+ public ActionResult Index()
+ {
+ return Content("");
+ }
+ }
+
+ public class AreaRouteConventionAttribute : RouteConventionAttributeBase
+ {
+ public override IEnumerable GetRouteAttributes(MethodInfo actionMethod)
+ {
+ yield return new GETAttribute(actionMethod.Name);
+ }
+
+ public override RouteAreaAttribute GetDefaultRouteArea(Type controllerType)
+ {
+ var areaName = controllerType.Namespace.ValueOr("").Split('.').Last();
+ return new RouteAreaAttribute(areaName);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http/DefaultHttpRouteConventionAttribute.cs b/src/AttributeRouting.Web.Http/DefaultHttpRouteConventionAttribute.cs
index 591352d..adf29f5 100644
--- a/src/AttributeRouting.Web.Http/DefaultHttpRouteConventionAttribute.cs
+++ b/src/AttributeRouting.Web.Http/DefaultHttpRouteConventionAttribute.cs
@@ -67,9 +67,9 @@ public override IEnumerable GetRouteAttributes(MethodInfo actio
}
}
- public override string GetDefaultRoutePrefix(MethodInfo actionMethod)
+ public override RoutePrefixAttribute GetDefaultRoutePrefix(Type controllerType)
{
- return actionMethod.DeclaringType.GetControllerName();
+ return new RoutePrefixAttribute(controllerType.GetControllerName());
}
private IRouteAttribute BuildRouteAttribute(HttpRouteConventionInfo convention)
diff --git a/src/AttributeRouting.Web.Mvc/RestfulRouteConventionAttribute.cs b/src/AttributeRouting.Web.Mvc/RestfulRouteConventionAttribute.cs
index 5a10329..f0699c7 100644
--- a/src/AttributeRouting.Web.Mvc/RestfulRouteConventionAttribute.cs
+++ b/src/AttributeRouting.Web.Mvc/RestfulRouteConventionAttribute.cs
@@ -1,72 +1,72 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using AttributeRouting.Framework;
-using AttributeRouting.Helpers;
-
-namespace AttributeRouting.Web.Mvc
-{
- ///
- /// Automatically generates RESTful-style routes for controller actions matching
- /// Index, New, Create, Show, Edit, Update, Delete, and Destroy.
- ///
- public class RestfulRouteConventionAttribute : RouteConventionAttributeBase
- {
- // Setup conventions
- private static readonly List Conventions = new List
- {
- new RestfulRouteConventionInfo("Index", "GET", ""),
- new RestfulRouteConventionInfo("New", "GET", "New"),
- new RestfulRouteConventionInfo("Create", "POST", ""),
- new RestfulRouteConventionInfo("Show", "GET", "{id}"),
- new RestfulRouteConventionInfo("Edit", "GET", "{id}/Edit"),
- new RestfulRouteConventionInfo("Update", "PUT", "{id}"),
- new RestfulRouteConventionInfo("Delete", "GET", "{id}/Delete"),
- new RestfulRouteConventionInfo("Destroy", "DELETE", "{id}")
- };
-
- public override IEnumerable GetRouteAttributes(MethodInfo actionMethod)
- {
- var convention = Conventions.SingleOrDefault(c => c.ActionName == actionMethod.Name);
- if (convention != null)
- yield return BuildRouteAttribute(convention);
- }
-
- public override string GetDefaultRoutePrefix(MethodInfo actionMethod)
- {
- return actionMethod.DeclaringType.GetControllerName();
- }
-
- private IRouteAttribute BuildRouteAttribute(RestfulRouteConventionInfo convention)
- {
- switch (convention.HttpMethod)
- {
- case "GET":
- return new GETAttribute(convention.Url);
- case "POST":
- return new POSTAttribute(convention.Url);
- case "PUT":
- return new PUTAttribute(convention.Url);
- case "DELETE":
- return new DELETEAttribute(convention.Url);
- default:
- throw new AttributeRoutingException("Unknown HTTP method \"{0}\".".FormatWith(convention.HttpMethod));
- }
- }
-
- private class RestfulRouteConventionInfo
- {
- public RestfulRouteConventionInfo(string actionName, string httpMethod, string url)
- {
- ActionName = actionName;
- HttpMethod = httpMethod;
- Url = url;
- }
-
- public string ActionName { get; private set; }
- public string HttpMethod { get; private set; }
- public string Url { get; private set; }
- }
- }
-}
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using AttributeRouting.Framework;
+using AttributeRouting.Helpers;
+
+namespace AttributeRouting.Web.Mvc
+{
+ ///
+ /// Automatically generates RESTful-style routes for controller actions matching
+ /// Index, New, Create, Show, Edit, Update, Delete, and Destroy.
+ ///
+ public class RestfulRouteConventionAttribute : RouteConventionAttributeBase
+ {
+ // Setup conventions
+ private static readonly List Conventions = new List
+ {
+ new RestfulRouteConventionInfo("Index", "GET", ""),
+ new RestfulRouteConventionInfo("New", "GET", "New"),
+ new RestfulRouteConventionInfo("Create", "POST", ""),
+ new RestfulRouteConventionInfo("Show", "GET", "{id}"),
+ new RestfulRouteConventionInfo("Edit", "GET", "{id}/Edit"),
+ new RestfulRouteConventionInfo("Update", "PUT", "{id}"),
+ new RestfulRouteConventionInfo("Delete", "GET", "{id}/Delete"),
+ new RestfulRouteConventionInfo("Destroy", "DELETE", "{id}")
+ };
+
+ public override IEnumerable GetRouteAttributes(MethodInfo actionMethod)
+ {
+ var convention = Conventions.SingleOrDefault(c => c.ActionName == actionMethod.Name);
+ if (convention != null)
+ yield return BuildRouteAttribute(convention);
+ }
+
+ public override RoutePrefixAttribute GetDefaultRoutePrefix(Type controllerType)
+ {
+ return new RoutePrefixAttribute(controllerType.GetControllerName());
+ }
+
+ private IRouteAttribute BuildRouteAttribute(RestfulRouteConventionInfo convention)
+ {
+ switch (convention.HttpMethod)
+ {
+ case "GET":
+ return new GETAttribute(convention.Url);
+ case "POST":
+ return new POSTAttribute(convention.Url);
+ case "PUT":
+ return new PUTAttribute(convention.Url);
+ case "DELETE":
+ return new DELETEAttribute(convention.Url);
+ default:
+ throw new AttributeRoutingException("Unknown HTTP method \"{0}\".".FormatWith(convention.HttpMethod));
+ }
+ }
+
+ private class RestfulRouteConventionInfo
+ {
+ public RestfulRouteConventionInfo(string actionName, string httpMethod, string url)
+ {
+ ActionName = actionName;
+ HttpMethod = httpMethod;
+ Url = url;
+ }
+
+ public string ActionName { get; private set; }
+ public string HttpMethod { get; private set; }
+ public string Url { get; private set; }
+ }
+ }
+}
diff --git a/src/AttributeRouting/Framework/RouteReflector.cs b/src/AttributeRouting/Framework/RouteReflector.cs
index cc9be33..ac45235 100644
--- a/src/AttributeRouting/Framework/RouteReflector.cs
+++ b/src/AttributeRouting/Framework/RouteReflector.cs
@@ -55,8 +55,16 @@ private IEnumerable BuildRouteSpecifications(IEnumerable(false)
let routeAreaAttribute = controllerType.GetCustomAttribute(true)
- let routePrefixAttribute = controllerType.GetCustomAttribute(true)
+ ?? convention.SafeGet(x => x.GetDefaultRouteArea(controllerType))
from actionMethod in controllerType.GetActionMethods(inheritActionsFromBaseController)
+ // NOTE: The oldConventionalRoutePrefix var is to support obsolete method.
+ // Once that method is removed, remove oldConventionalRoutePrefix from consideration,
+ // and move the let routePrefixAttribute op above the loop inside the actionMethed.
+ let oldConventionalRoutePrefix = convention.SafeGet(x => x.GetDefaultRoutePrefix(actionMethod))
+ let routePrefixAttribute = controllerType.GetCustomAttribute(true)
+ ?? (oldConventionalRoutePrefix.HasValue()
+ ? new RoutePrefixAttribute(oldConventionalRoutePrefix)
+ : convention.SafeGet(x => x.GetDefaultRoutePrefix(controllerType)))
from routeAttribute in GetRouteAttributes(actionMethod, convention)
let routeName = routeAttribute.RouteName
let subdomain = GetAreaSubdomain(routeAreaAttribute)
@@ -70,14 +78,14 @@ from routeAttribute in GetRouteAttributes(actionMethod, convention)
let sitePrecedence = GetSortableOrder(routeAttribute.SitePrecedence)
let controllerPrecedence = GetSortableOrder(routeAttribute.ControllerPrecedence)
let actionPrecedence = GetSortableOrder(routeAttribute.ActionPrecedence)
- orderby sitePrecedence , controllerIndex , controllerPrecedence , actionPrecedence
+ orderby sitePrecedence, controllerIndex, controllerPrecedence, actionPrecedence
select new RouteSpecification
{
AreaName = routeAreaAttribute.SafeGet(a => a.AreaName),
AreaUrl = GetAreaUrl(routeAreaAttribute, subdomain),
AreaUrlTranslationKey = routeAreaAttribute.SafeGet(a => a.TranslationKey),
Subdomain = subdomain,
- RoutePrefixUrl = GetRoutePrefix(routePrefixAttribute, actionMethod, convention),
+ RoutePrefixUrl = routePrefixAttribute.SafeGet(p => p.Url),
RoutePrefixUrlTranslationKey = routePrefixAttribute.SafeGet(a => a.TranslationKey),
ControllerType = controllerType,
ControllerName = controllerType.GetControllerName(),
@@ -171,26 +179,6 @@ private string GetAreaSubdomain(RouteAreaAttribute routeAreaAttribute)
return routeAreaAttribute.Subdomain;
}
- ///
- /// Gets a controller's route prefix URL.
- ///
- /// The for the controller.
- /// The for an action.
- /// The for the controller.
- /// The route prefix URL to apply against the action.
- private static string GetRoutePrefix(RoutePrefixAttribute routePrefixAttribute, MethodInfo actionMethod, RouteConventionAttributeBase convention)
- {
- // Return an explicitly defined route prefix, if defined
- if (routePrefixAttribute != null)
- return routePrefixAttribute.Url;
-
- // Otherwise, if this is a convention-based controller, get the convention-based prefix
- if (convention != null)
- return convention.GetDefaultRoutePrefix(actionMethod);
-
- return null;
- }
-
///
/// Gets the sortable order for the given value.
///
diff --git a/src/AttributeRouting/RouteConventionAttributeBase.cs b/src/AttributeRouting/RouteConventionAttributeBase.cs
index 2c43a57..219b4bf 100644
--- a/src/AttributeRouting/RouteConventionAttributeBase.cs
+++ b/src/AttributeRouting/RouteConventionAttributeBase.cs
@@ -13,18 +13,41 @@ public abstract class RouteConventionAttributeBase : Attribute
///
/// Gets the RouteAttributes to be applied to the given action method.
///
- ///
- ///
+ /// The action method.
+ /// A collection of routes for the action.
public abstract IEnumerable GetRouteAttributes(MethodInfo actionMethod);
///
/// Gets the default route prefix to use if no RoutePrefix is applied on the controller.
///
- ///
- ///
+ /// The action method.
+ /// A default prefix to use for the actions in the controller.
+ [Obsolete]
public virtual string GetDefaultRoutePrefix(MethodInfo actionMethod)
{
return "";
}
+
+ ///
+ /// Gets a to apply to the controller
+ /// if no explicit route prefix is specified.
+ ///
+ /// The controller type.
+ /// A .
+ public virtual RoutePrefixAttribute GetDefaultRoutePrefix(Type controllerType)
+ {
+ return null;
+ }
+
+ ///
+ /// Gets a to apply to the controller
+ /// if no explicit route area is specified.
+ ///
+ /// The controller type.
+ /// A .
+ public virtual RouteAreaAttribute GetDefaultRouteArea(Type controllerType)
+ {
+ return null;
+ }
}
}
\ No newline at end of file
From 39eaefd1803ec39787b2b95bc51bbec1cdc75cf7 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sat, 8 Dec 2012 17:35:21 -0700
Subject: [PATCH 10/97] #164 - added default ctor to route attribute in
AttributeRouting.Web.Mvc. These default ctors will end up creating routes
that use the name of the action as the route url.
---
.../Features/StandardUsage.feature | 47 ++++---
.../Features/StandardUsage.feature.cs | 36 +++---
.../Subjects/RouteAreasControllers.cs | 1 -
.../Subjects/StandardUsageController.cs | 30 +++++
.../Controllers/AccountController.cs | 1 -
.../Controllers/DangerController.cs | 1 -
.../Controllers/RenderActionController.cs | 1 -
.../Controllers/RestfulController.cs | 1 -
.../DELETEAttribute.cs | 6 +
src/AttributeRouting.Web.Mvc/GETAttribute.cs | 6 +
src/AttributeRouting.Web.Mvc/POSTAttribute.cs | 6 +
src/AttributeRouting.Web.Mvc/PUTAttribute.cs | 6 +
.../RouteAttribute.cs | 28 +++-
.../Framework/RouteReflector.cs | 5 +-
.../Helpers/StringExtensions.cs | 122 +++++++++---------
15 files changed, 190 insertions(+), 107 deletions(-)
diff --git a/src/AttributeRouting.Specs/Features/StandardUsage.feature b/src/AttributeRouting.Specs/Features/StandardUsage.feature
index ea75c70..b986a10 100644
--- a/src/AttributeRouting.Specs/Features/StandardUsage.feature
+++ b/src/AttributeRouting.Specs/Features/StandardUsage.feature
@@ -10,14 +10,21 @@ Scenario Outline: Generating routes for an MVC controller
And the namespace is "AttributeRouting.Specs.Subjects"
Examples:
- | method | action | url |
- | GET | Index | |
- | HEAD | Index | |
- | POST | Create | Create |
- | PUT | Update | Update/{id} |
- | DELETE | Destroy | Destroy/{id} |
- | GET | Wildcards | Wildcards/{*pathInfo} |
- | | AnyVerb | AnyVerb |
+ | method | action | url |
+ | GET | Index | |
+ | HEAD | Index | |
+ | POST | Create | Create |
+ | PUT | Update | Update/{id} |
+ | DELETE | Destroy | Destroy/{id} |
+ | GET | Wildcards | Wildcards/{*pathInfo} |
+ | | AnyVerb | AnyVerb |
+ # The URL when using the default ctor should simply be the action name
+ | GET | GetDefault | GetDefault |
+ | HEAD | GetDefault | GetDefault |
+ | PUT | PutDefault | PutDefault |
+ | POST | PostDefault | PostDefault |
+ | DELETE | DeleteDefault | DeleteDefault |
+ | | RouteDefault | RouteDefault |
Scenario Outline: Generating routes for an API controller
Given I have registered the routes for the HttpStandardUsageController
@@ -29,18 +36,18 @@ Scenario Outline: Generating routes for an API controller
And the namespace is "AttributeRouting.Specs.Subjects.Http"
Examples:
- | method | action | url |
- | GET | Get | api |
- | HEAD | Get | api |
- | OPTIONS | Get | api |
- | POST | Post | api |
- | OPTIONS | Post | api |
- | PUT | Put | api/{id} |
- | OPTIONS | Put | api/{id} |
- | DELETE | Delete | api/{id} |
- | OPTIONS | Delete | api/{id} |
- | GET | Wildcards | api/Wildcards/{*pathInfo} |
- | | AnyVerb | api/AnyVerb |
+ | method | action | url |
+ | GET | Get | api |
+ | HEAD | Get | api |
+ | OPTIONS | Get | api |
+ | POST | Post | api |
+ | OPTIONS | Post | api |
+ | PUT | Put | api/{id} |
+ | OPTIONS | Put | api/{id} |
+ | DELETE | Delete | api/{id} |
+ | OPTIONS | Delete | api/{id} |
+ | GET | Wildcards | api/Wildcards/{*pathInfo} |
+ | | AnyVerb | api/AnyVerb |
Scenario: Responding to OPTIONS requests in an API controller
Given I have registered the routes for the HttpStandardUsageController
diff --git a/src/AttributeRouting.Specs/Features/StandardUsage.feature.cs b/src/AttributeRouting.Specs/Features/StandardUsage.feature.cs
index c7b3042..2721111 100644
--- a/src/AttributeRouting.Specs/Features/StandardUsage.feature.cs
+++ b/src/AttributeRouting.Specs/Features/StandardUsage.feature.cs
@@ -1,9 +1,9 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.9.1.84
+// SpecFlow Version:1.9.0.77
// SpecFlow Generator Version:1.9.0.0
-// Runtime Version:4.0.30319.17929
+// Runtime Version:4.0.30319.18010
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.1.84")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Standard Usage")]
@@ -73,6 +73,12 @@ public virtual void ScenarioCleanup()
[NUnit.Framework.TestCaseAttribute("DELETE", "Destroy", "Destroy/{id}", null)]
[NUnit.Framework.TestCaseAttribute("GET", "Wildcards", "Wildcards/{*pathInfo}", null)]
[NUnit.Framework.TestCaseAttribute("", "AnyVerb", "AnyVerb", null)]
+ [NUnit.Framework.TestCaseAttribute("GET", "GetDefault", "GetDefault", null)]
+ [NUnit.Framework.TestCaseAttribute("HEAD", "GetDefault", "GetDefault", null)]
+ [NUnit.Framework.TestCaseAttribute("PUT", "PutDefault", "PutDefault", null)]
+ [NUnit.Framework.TestCaseAttribute("POST", "PostDefault", "PostDefault", null)]
+ [NUnit.Framework.TestCaseAttribute("DELETE", "DeleteDefault", "DeleteDefault", null)]
+ [NUnit.Framework.TestCaseAttribute("", "RouteDefault", "RouteDefault", null)]
public virtual void GeneratingRoutesForAnMVCController(string method, string action, string url, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes for an MVC controller", exampleTags);
@@ -112,21 +118,21 @@ public virtual void GeneratingRoutesForAnMVCController(string method, string act
public virtual void GeneratingRoutesForAnAPIController(string method, string action, string url, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes for an API controller", exampleTags);
-#line 22
+#line 29
this.ScenarioSetup(scenarioInfo);
-#line 23
+#line 30
testRunner.Given("I have registered the routes for the HttpStandardUsageController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 24
+#line 31
testRunner.When(string.Format("I fetch the routes for the HttpStandardUsageController\'s {0} action", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 25
+#line 32
testRunner.Then(string.Format("the route is constrained to {0} requests", method), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 26
+#line 33
testRunner.And(string.Format("the route url is \"{0}\"", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 27
+#line 34
testRunner.And("the default for \"controller\" is \"HttpStandardUsage\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 28
+#line 35
testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 29
+#line 36
testRunner.And("the namespace is \"AttributeRouting.Specs.Subjects.Http\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
@@ -137,13 +143,13 @@ public virtual void GeneratingRoutesForAnAPIController(string method, string act
public virtual void RespondingToOPTIONSRequestsInAnAPIController()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Responding to OPTIONS requests in an API controller", ((string[])(null)));
-#line 45
+#line 52
this.ScenarioSetup(scenarioInfo);
-#line 46
+#line 53
testRunner.Given("I have registered the routes for the HttpStandardUsageController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 47
+#line 54
testRunner.When("an OPTIONS request for \"api\" is made", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 48
+#line 55
testRunner.Then("the Get action is matched", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
diff --git a/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs b/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs
index 401ec08..dced645 100644
--- a/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs
@@ -1,5 +1,4 @@
using System.Web.Mvc;
-using AttributeRouting.Web;
using AttributeRouting.Web.Mvc;
namespace AttributeRouting.Specs.Subjects
diff --git a/src/AttributeRouting.Specs/Subjects/StandardUsageController.cs b/src/AttributeRouting.Specs/Subjects/StandardUsageController.cs
index b26e5eb..15e6a06 100644
--- a/src/AttributeRouting.Specs/Subjects/StandardUsageController.cs
+++ b/src/AttributeRouting.Specs/Subjects/StandardUsageController.cs
@@ -41,5 +41,35 @@ public ActionResult AnyVerb()
{
return Content("");
}
+
+ [GET]
+ public ActionResult GetDefault()
+ {
+ return Content("");
+ }
+
+ [POST]
+ public ActionResult PostDefault()
+ {
+ return Content("");
+ }
+
+ [PUT]
+ public ActionResult PutDefault()
+ {
+ return Content("");
+ }
+
+ [DELETE]
+ public ActionResult DeleteDefault()
+ {
+ return Content("");
+ }
+
+ [Route]
+ public ActionResult RouteDefault()
+ {
+ return Content("");
+ }
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests.Web/Controllers/AccountController.cs b/src/AttributeRouting.Tests.Web/Controllers/AccountController.cs
index 2ccdc94..2579f81 100644
--- a/src/AttributeRouting.Tests.Web/Controllers/AccountController.cs
+++ b/src/AttributeRouting.Tests.Web/Controllers/AccountController.cs
@@ -3,7 +3,6 @@
using System.Web.Routing;
using System.Web.Security;
using AttributeRouting.Tests.Web.Models;
-using AttributeRouting.Web;
using AttributeRouting.Web.Mvc;
namespace AttributeRouting.Tests.Web.Controllers
diff --git a/src/AttributeRouting.Tests.Web/Controllers/DangerController.cs b/src/AttributeRouting.Tests.Web/Controllers/DangerController.cs
index 8f568d6..0906b04 100644
--- a/src/AttributeRouting.Tests.Web/Controllers/DangerController.cs
+++ b/src/AttributeRouting.Tests.Web/Controllers/DangerController.cs
@@ -1,5 +1,4 @@
using System.Web.Mvc;
-using AttributeRouting.Web;
using AttributeRouting.Web.Mvc;
namespace AttributeRouting.Tests.Web.Controllers
diff --git a/src/AttributeRouting.Tests.Web/Controllers/RenderActionController.cs b/src/AttributeRouting.Tests.Web/Controllers/RenderActionController.cs
index 9974138..d14ce0f 100644
--- a/src/AttributeRouting.Tests.Web/Controllers/RenderActionController.cs
+++ b/src/AttributeRouting.Tests.Web/Controllers/RenderActionController.cs
@@ -1,5 +1,4 @@
using System.Web.Mvc;
-using AttributeRouting.Web;
using AttributeRouting.Web.Mvc;
namespace AttributeRouting.Tests.Web.Controllers
diff --git a/src/AttributeRouting.Tests.Web/Controllers/RestfulController.cs b/src/AttributeRouting.Tests.Web/Controllers/RestfulController.cs
index 7c979c7..fb11ad2 100644
--- a/src/AttributeRouting.Tests.Web/Controllers/RestfulController.cs
+++ b/src/AttributeRouting.Tests.Web/Controllers/RestfulController.cs
@@ -1,5 +1,4 @@
using System.Web.Mvc;
-using AttributeRouting.Web;
using AttributeRouting.Web.Mvc;
namespace AttributeRouting.Tests.Web.Controllers
diff --git a/src/AttributeRouting.Web.Mvc/DELETEAttribute.cs b/src/AttributeRouting.Web.Mvc/DELETEAttribute.cs
index b98eb03..d431a6b 100644
--- a/src/AttributeRouting.Web.Mvc/DELETEAttribute.cs
+++ b/src/AttributeRouting.Web.Mvc/DELETEAttribute.cs
@@ -7,6 +7,12 @@ namespace AttributeRouting.Web.Mvc
///
public class DELETEAttribute : RouteAttribute
{
+ ///
+ /// Specify a route for DELETE request.
+ /// The route URL will be the name of the action.
+ ///
+ public DELETEAttribute() : base(HttpVerbs.Delete) {}
+
///
/// Specify a route for DELETE request.
///
diff --git a/src/AttributeRouting.Web.Mvc/GETAttribute.cs b/src/AttributeRouting.Web.Mvc/GETAttribute.cs
index 903982f..5adae1b 100644
--- a/src/AttributeRouting.Web.Mvc/GETAttribute.cs
+++ b/src/AttributeRouting.Web.Mvc/GETAttribute.cs
@@ -7,6 +7,12 @@ namespace AttributeRouting.Web.Mvc
///
public class GETAttribute : RouteAttribute
{
+ ///
+ /// Specify a route for a GET request.
+ /// The route URL will be the name of the action.
+ ///
+ public GETAttribute() : base(HttpVerbs.Get, HttpVerbs.Head) {}
+
///
/// Specify a route for a GET request.
///
diff --git a/src/AttributeRouting.Web.Mvc/POSTAttribute.cs b/src/AttributeRouting.Web.Mvc/POSTAttribute.cs
index 5abc877..eb6bf39 100644
--- a/src/AttributeRouting.Web.Mvc/POSTAttribute.cs
+++ b/src/AttributeRouting.Web.Mvc/POSTAttribute.cs
@@ -7,6 +7,12 @@ namespace AttributeRouting.Web.Mvc
///
public class POSTAttribute : RouteAttribute
{
+ ///
+ /// Specify a route for a POST request.
+ /// The route URL will be the name of the action.
+ ///
+ public POSTAttribute() : base(HttpVerbs.Post) {}
+
///
/// Specify a route for a POST request.
///
diff --git a/src/AttributeRouting.Web.Mvc/PUTAttribute.cs b/src/AttributeRouting.Web.Mvc/PUTAttribute.cs
index f179bbf..71f9726 100644
--- a/src/AttributeRouting.Web.Mvc/PUTAttribute.cs
+++ b/src/AttributeRouting.Web.Mvc/PUTAttribute.cs
@@ -7,6 +7,12 @@ namespace AttributeRouting.Web.Mvc
///
public class PUTAttribute : RouteAttribute
{
+ ///
+ /// Specify a route for a PUT request.
+ /// The route URL will be the name of the action.
+ ///
+ public PUTAttribute() : base(HttpVerbs.Put) {}
+
///
/// Specify a route for a PUT request.
///
diff --git a/src/AttributeRouting.Web.Mvc/RouteAttribute.cs b/src/AttributeRouting.Web.Mvc/RouteAttribute.cs
index bc5e180..cf37cda 100644
--- a/src/AttributeRouting.Web.Mvc/RouteAttribute.cs
+++ b/src/AttributeRouting.Web.Mvc/RouteAttribute.cs
@@ -12,19 +12,39 @@ namespace AttributeRouting.Web.Mvc
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class RouteAttribute : ActionMethodSelectorAttribute, IRouteAttribute
{
+ ///
+ /// Specify the route information for an action.
+ /// The route URL will be the name of the action.
+ ///
+ public RouteAttribute()
+ {
+ HttpMethods = new string[0];
+ ActionPrecedence = int.MaxValue;
+ ControllerPrecedence = int.MaxValue;
+ SitePrecedence = int.MaxValue;
+ }
+
///
/// Specify the route information for an action.
///
/// The url that is associated with this action
public RouteAttribute(string routeUrl)
+ : this()
{
if (routeUrl == null) throw new ArgumentNullException("routeUrl");
RouteUrl = routeUrl;
- HttpMethods = new string[0];
- ActionPrecedence = int.MaxValue;
- ControllerPrecedence = int.MaxValue;
- SitePrecedence = int.MaxValue;
+ }
+
+ ///
+ /// Specify the route information for an action.
+ /// The route URL will be the name of the action.
+ ///
+ /// The httpMethods against which to constrain the route
+ public RouteAttribute(params HttpVerbs[] allowedMethods)
+ : this()
+ {
+ HttpMethods = allowedMethods.Select(m => m.ToString().ToUpperInvariant()).ToArray();
}
///
diff --git a/src/AttributeRouting/Framework/RouteReflector.cs b/src/AttributeRouting/Framework/RouteReflector.cs
index ac45235..91223f4 100644
--- a/src/AttributeRouting/Framework/RouteReflector.cs
+++ b/src/AttributeRouting/Framework/RouteReflector.cs
@@ -69,6 +69,7 @@ from routeAttribute in GetRouteAttributes(actionMethod, convention)
let routeName = routeAttribute.RouteName
let subdomain = GetAreaSubdomain(routeAreaAttribute)
let isAsyncController = controllerType.IsAsyncController()
+ let actionName = GetActionName(actionMethod, isAsyncController)
/* controlling precedence:
* site precedence of route >
* controller index >
@@ -89,8 +90,8 @@ from routeAttribute in GetRouteAttributes(actionMethod, convention)
RoutePrefixUrlTranslationKey = routePrefixAttribute.SafeGet(a => a.TranslationKey),
ControllerType = controllerType,
ControllerName = controllerType.GetControllerName(),
- ActionName = GetActionName(actionMethod, isAsyncController),
- RouteUrl = routeAttribute.RouteUrl,
+ ActionName = actionName,
+ RouteUrl = routeAttribute.RouteUrl ?? actionName,
RouteUrlTranslationKey = routeAttribute.TranslationKey,
HttpMethods = routeAttribute.HttpMethods,
RouteName = routeName,
diff --git a/src/AttributeRouting/Helpers/StringExtensions.cs b/src/AttributeRouting/Helpers/StringExtensions.cs
index 2cc3a9e..e5ac605 100644
--- a/src/AttributeRouting/Helpers/StringExtensions.cs
+++ b/src/AttributeRouting/Helpers/StringExtensions.cs
@@ -1,67 +1,67 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text.RegularExpressions;
-
-namespace AttributeRouting.Helpers
-{
- public static class StringExtensions
- {
- public static bool ValueEquals(this string s, string other)
- {
- if (s == null)
- return other == null;
-
- return s.Equals(other, StringComparison.OrdinalIgnoreCase);
- }
-
- public static bool HasValue(this string s)
- {
- return !String.IsNullOrWhiteSpace(s);
- }
-
- public static bool HasNoValue(this string s)
- {
- return String.IsNullOrWhiteSpace(s);
- }
-
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace AttributeRouting.Helpers
+{
+ public static class StringExtensions
+ {
+ public static bool ValueEquals(this string s, string other)
+ {
+ if (s == null)
+ return other == null;
+
+ return s.Equals(other, StringComparison.OrdinalIgnoreCase);
+ }
+
+ public static bool HasValue(this string s)
+ {
+ return !String.IsNullOrWhiteSpace(s);
+ }
+
+ public static bool HasNoValue(this string s)
+ {
+ return String.IsNullOrWhiteSpace(s);
+ }
+
public static string ValueOr(this string s, string otherValue)
{
if (s.HasValue())
return s;
return otherValue;
- }
-
- public static string FormatWith(this string s, params object[] args)
- {
- return String.Format(s, args);
- }
-
- public static string[] SplitAndTrim(this string s, params string[] separator)
- {
- if (!s.HasValue())
- return null;
-
- return s.Split(separator, StringSplitOptions.RemoveEmptyEntries).Select(i => i.Trim()).ToArray();
- }
-
- public static bool IsValidUrl(this string s, bool allowTokens = false)
- {
- var urlParts = s.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
-
- var invalidUrlPatterns = new List
- {
- @"[#%&:<>/{0}]".FormatWith(allowTokens ? null : @"\\\+\{\}?\*"),
- @"\.\.",
- @"\.$",
- @"^ ",
- @" $"
- };
-
- var invalidUrlPattern = String.Join("|", invalidUrlPatterns);
-
- return !urlParts.Any(p => Regex.IsMatch(p, invalidUrlPattern));
- }
- }
-}
+ }
+
+ public static string FormatWith(this string s, params object[] args)
+ {
+ return String.Format(s, args);
+ }
+
+ public static string[] SplitAndTrim(this string s, params string[] separator)
+ {
+ if (!s.HasValue())
+ return null;
+
+ return s.Split(separator, StringSplitOptions.RemoveEmptyEntries).Select(i => i.Trim()).ToArray();
+ }
+
+ public static bool IsValidUrl(this string s, bool allowTokens = false)
+ {
+ var urlParts = s.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
+
+ var invalidUrlPatterns = new List
+ {
+ @"[#%&:<>/{0}]".FormatWith(allowTokens ? null : @"\\\+\{\}?\*"),
+ @"\.\.",
+ @"\.$",
+ @"^ ",
+ @" $"
+ };
+
+ var invalidUrlPattern = String.Join("|", invalidUrlPatterns);
+
+ return !urlParts.Any(p => Regex.IsMatch(p, invalidUrlPattern));
+ }
+ }
+}
From 17e541f34b1223ec49251038929dc0c2ac842b54 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sat, 8 Dec 2012 17:58:04 -0700
Subject: [PATCH 11/97] #164 - added default ctor to RouteAreaAttribute. This
default ctor will end up using the last section of the controller's namespace
as the value of the aarea name and anrea url prefix.
---
.../AttributeRouting.Specs.csproj | 2 -
.../Features/RouteAreas.feature | 106 ++++++----------
.../Features/RouteAreas.feature.cs | 120 +++++++-----------
.../Subjects/Http/HttpAreasController.cs | 33 -----
.../Http/HttpExplicitAreaUrlController.cs | 21 ---
.../Subjects/RouteAreasControllers.cs | 10 ++
.../Framework/RouteReflector.cs | 41 +++++-
.../Helpers/ObjectExtensions.cs | 52 ++++----
.../Helpers/ReflectionExtensions.cs | 6 +
src/AttributeRouting/RouteAreaAttribute.cs | 7 +
10 files changed, 172 insertions(+), 226 deletions(-)
delete mode 100644 src/AttributeRouting.Specs/Subjects/Http/HttpAreasController.cs
delete mode 100644 src/AttributeRouting.Specs/Subjects/Http/HttpExplicitAreaUrlController.cs
diff --git a/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj b/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
index e583024..a163338 100644
--- a/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
+++ b/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
@@ -147,9 +147,7 @@
-
-
diff --git a/src/AttributeRouting.Specs/Features/RouteAreas.feature b/src/AttributeRouting.Specs/Features/RouteAreas.feature
index 1122ae8..6294193 100644
--- a/src/AttributeRouting.Specs/Features/RouteAreas.feature
+++ b/src/AttributeRouting.Specs/Features/RouteAreas.feature
@@ -1,67 +1,41 @@
-Feature: Route Areas
-
-Scenario: Generating area routes
- # MVC
+Feature: Route Areas
+
+Scenario: Generating area routes
Given I have registered the routes for the AreasController
- When I fetch the routes for the Areas controller's Index action
- Then the route url is "Area/Index"
- And the data token for "area" is "Area"
- # Web API
- Given I have registered the routes for the HttpAreasController
- When I fetch the routes for the HttpAreas controller's Get action
- Then the route url is "ApiArea/Get"
- And the data token for "area" is "ApiArea"
-
-Scenario: Generating area routes when route urls specify a duplicate area prefix
- # MVC
- Given I have registered the routes for the AreasController
- When I fetch the routes for the Areas controller's DuplicatePrefix action
- Then the route url is "Area/DuplicatePrefix"
- # Web API
- Given I have registered the routes for the HttpAreasController
- When I fetch the routes for the HttpAreas controller's DuplicatePrefix action
- Then the route url is "ApiArea/DuplicatePrefix"
-
-Scenario: Generating absolute routes when a route area is defined
- # MVC
- Given I have registered the routes for the AreasController
- When I fetch the routes for the Areas controller's Absolute action
- Then the route url is "AreaAbsolute"
- # Web API
- Given I have registered the routes for the HttpAreasController
- When I fetch the routes for the HttpAreas controller's Absolute action
- Then the route url is "ApiAreaAbsolute"
-
-Scenario: Generating area routes when route url starts with the area prefix
- # MVC
- Given I have registered the routes for the AreasController
- When I fetch the routes for the Areas controller's RouteBeginsWithAreaName action
- Then the route url is "Area/Areas"
- # Web API
- Given I have registered the routes for the HttpAreasController
- When I fetch the routes for the HttpAreas controller's RouteBeginsWithAreaName action
- Then the route url is "ApiArea/ApiAreas"
-
-Scenario: Generating area routes with an explicit area url
- # MVC
- Given I have registered the routes for the ExplicitAreaUrlController
- When I fetch the routes for the ExplicitAreaUrl controller's Index action
- Then the route url is "ExplicitArea/Index"
- And the data token for "area" is "Area"
- # Web API
- Given I have registered the routes for the HttpExplicitAreaUrlController
- When I fetch the routes for the HttpExplicitAreaUrl controller's Get action
- Then the route url is "ApiExplicitArea/Get"
- And the data token for "area" is "ApiArea"
-
-Scenario: Generating area routes with an explicit area url when route urls specify a duplicate area prefix
- # MVC
- Given I have registered the routes for the ExplicitAreaUrlController
- When I fetch the routes for the ExplicitAreaUrl controller's DuplicatePrefix action
- Then the route url is "ExplicitArea/DuplicatePrefix"
- And the data token for "area" is "Area"
- # Web API
- Given I have registered the routes for the HttpExplicitAreaUrlController
- When I fetch the routes for the HttpExplicitAreaUrl controller's DuplicatePrefix action
- Then the route url is "ApiExplicitArea/DuplicatePrefix"
- And the data token for "area" is "ApiArea"
+ When I fetch the routes for the Areas controller's Index action
+ Then the route url is "Area/Index"
+ And the data token for "area" is "Area"
+
+Scenario: Generating area routes when route urls specify a duplicate area prefix
+ Given I have registered the routes for the AreasController
+ When I fetch the routes for the Areas controller's DuplicatePrefix action
+ Then the route url is "Area/DuplicatePrefix"
+
+Scenario: Generating absolute routes when a route area is defined
+ Given I have registered the routes for the AreasController
+ When I fetch the routes for the Areas controller's Absolute action
+ Then the route url is "AreaAbsolute"
+
+Scenario: Generating area routes when route url starts with the area prefix
+ Given I have registered the routes for the AreasController
+ When I fetch the routes for the Areas controller's RouteBeginsWithAreaName action
+ Then the route url is "Area/Areas"
+
+Scenario: Generating area routes with an explicit area url
+ Given I have registered the routes for the ExplicitAreaUrlController
+ When I fetch the routes for the ExplicitAreaUrl controller's Index action
+ Then the route url is "ExplicitArea/Index"
+ And the data token for "area" is "Area"
+
+Scenario: Generating area routes with an explicit area url when route urls specify a duplicate area prefix
+ Given I have registered the routes for the ExplicitAreaUrlController
+ When I fetch the routes for the ExplicitAreaUrl controller's DuplicatePrefix action
+ Then the route url is "ExplicitArea/DuplicatePrefix"
+ And the data token for "area" is "Area"
+
+Scenario: Generating area routes with the default ctor of the RouteAreaAttribute
+ Given I have registered the routes for the DefaultRouteAreaController
+ When I fetch the routes for the DefaultRouteArea controller's Index action
+ Then the route url is "Subjects/Index"
+ And the data token for "area" is "Subjects"
+
diff --git a/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs b/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs
index 63879f1..2d1d851 100644
--- a/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs
@@ -1,9 +1,9 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.9.1.84
+// SpecFlow Version:1.9.0.77
// SpecFlow Generator Version:1.9.0.0
-// Runtime Version:4.0.30319.17929
+// Runtime Version:4.0.30319.18010
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.1.84")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Route Areas")]
@@ -71,22 +71,14 @@ public virtual void GeneratingAreaRoutes()
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating area routes", ((string[])(null)));
#line 3
this.ScenarioSetup(scenarioInfo);
-#line 5
+#line 4
testRunner.Given("I have registered the routes for the AreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 6
+#line 5
testRunner.When("I fetch the routes for the Areas controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 7
+#line 6
testRunner.Then("the route url is \"Area/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 8
+#line 7
testRunner.And("the data token for \"area\" is \"Area\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 10
- testRunner.Given("I have registered the routes for the HttpAreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 11
- testRunner.When("I fetch the routes for the HttpAreas controller\'s Get action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 12
- testRunner.Then("the route url is \"ApiArea/Get\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 13
- testRunner.And("the data token for \"area\" is \"ApiArea\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -96,20 +88,14 @@ public virtual void GeneratingAreaRoutes()
public virtual void GeneratingAreaRoutesWhenRouteUrlsSpecifyADuplicateAreaPrefix()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating area routes when route urls specify a duplicate area prefix", ((string[])(null)));
-#line 15
+#line 9
this.ScenarioSetup(scenarioInfo);
-#line 17
+#line 10
testRunner.Given("I have registered the routes for the AreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 18
+#line 11
testRunner.When("I fetch the routes for the Areas controller\'s DuplicatePrefix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 19
+#line 12
testRunner.Then("the route url is \"Area/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 21
- testRunner.Given("I have registered the routes for the HttpAreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 22
- testRunner.When("I fetch the routes for the HttpAreas controller\'s DuplicatePrefix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 23
- testRunner.Then("the route url is \"ApiArea/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -119,20 +105,14 @@ public virtual void GeneratingAreaRoutesWhenRouteUrlsSpecifyADuplicateAreaPrefix
public virtual void GeneratingAbsoluteRoutesWhenARouteAreaIsDefined()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating absolute routes when a route area is defined", ((string[])(null)));
-#line 25
+#line 14
this.ScenarioSetup(scenarioInfo);
-#line 27
+#line 15
testRunner.Given("I have registered the routes for the AreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 28
+#line 16
testRunner.When("I fetch the routes for the Areas controller\'s Absolute action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 29
+#line 17
testRunner.Then("the route url is \"AreaAbsolute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 31
- testRunner.Given("I have registered the routes for the HttpAreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 32
- testRunner.When("I fetch the routes for the HttpAreas controller\'s Absolute action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 33
- testRunner.Then("the route url is \"ApiAreaAbsolute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -142,20 +122,14 @@ public virtual void GeneratingAbsoluteRoutesWhenARouteAreaIsDefined()
public virtual void GeneratingAreaRoutesWhenRouteUrlStartsWithTheAreaPrefix()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating area routes when route url starts with the area prefix", ((string[])(null)));
-#line 35
+#line 19
this.ScenarioSetup(scenarioInfo);
-#line 37
+#line 20
testRunner.Given("I have registered the routes for the AreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 38
+#line 21
testRunner.When("I fetch the routes for the Areas controller\'s RouteBeginsWithAreaName action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 39
+#line 22
testRunner.Then("the route url is \"Area/Areas\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 41
- testRunner.Given("I have registered the routes for the HttpAreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 42
- testRunner.When("I fetch the routes for the HttpAreas controller\'s RouteBeginsWithAreaName action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 43
- testRunner.Then("the route url is \"ApiArea/ApiAreas\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
@@ -165,24 +139,16 @@ public virtual void GeneratingAreaRoutesWhenRouteUrlStartsWithTheAreaPrefix()
public virtual void GeneratingAreaRoutesWithAnExplicitAreaUrl()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating area routes with an explicit area url", ((string[])(null)));
-#line 45
+#line 24
this.ScenarioSetup(scenarioInfo);
-#line 47
+#line 25
testRunner.Given("I have registered the routes for the ExplicitAreaUrlController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 48
+#line 26
testRunner.When("I fetch the routes for the ExplicitAreaUrl controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 49
+#line 27
testRunner.Then("the route url is \"ExplicitArea/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 50
+#line 28
testRunner.And("the data token for \"area\" is \"Area\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 52
- testRunner.Given("I have registered the routes for the HttpExplicitAreaUrlController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 53
- testRunner.When("I fetch the routes for the HttpExplicitAreaUrl controller\'s Get action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 54
- testRunner.Then("the route url is \"ApiExplicitArea/Get\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 55
- testRunner.And("the data token for \"area\" is \"ApiArea\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -194,25 +160,35 @@ public virtual void GeneratingAreaRoutesWithAnExplicitAreaUrlWhenRouteUrlsSpecif
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating area routes with an explicit area url when route urls specify a duplic" +
"ate area prefix", ((string[])(null)));
-#line 57
+#line 30
this.ScenarioSetup(scenarioInfo);
-#line 59
+#line 31
testRunner.Given("I have registered the routes for the ExplicitAreaUrlController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 60
+#line 32
testRunner.When("I fetch the routes for the ExplicitAreaUrl controller\'s DuplicatePrefix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 61
+#line 33
testRunner.Then("the route url is \"ExplicitArea/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 62
+#line 34
testRunner.And("the data token for \"area\" is \"Area\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 64
- testRunner.Given("I have registered the routes for the HttpExplicitAreaUrlController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 65
- testRunner.When("I fetch the routes for the HttpExplicitAreaUrl controller\'s DuplicatePrefix actio" +
- "n", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 66
- testRunner.Then("the route url is \"ApiExplicitArea/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 67
- testRunner.And("the data token for \"area\" is \"ApiArea\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Generating area routes with the default ctor of the RouteAreaAttribute")]
+ public virtual void GeneratingAreaRoutesWithTheDefaultCtorOfTheRouteAreaAttribute()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating area routes with the default ctor of the RouteAreaAttribute", ((string[])(null)));
+#line 36
+this.ScenarioSetup(scenarioInfo);
+#line 37
+ testRunner.Given("I have registered the routes for the DefaultRouteAreaController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 38
+ testRunner.When("I fetch the routes for the DefaultRouteArea controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 39
+ testRunner.Then("the route url is \"Subjects/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 40
+ testRunner.And("the data token for \"area\" is \"Subjects\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Subjects/Http/HttpAreasController.cs b/src/AttributeRouting.Specs/Subjects/Http/HttpAreasController.cs
deleted file mode 100644
index 04299d5..0000000
--- a/src/AttributeRouting.Specs/Subjects/Http/HttpAreasController.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.Web.Http;
-using AttributeRouting.Web.Http;
-
-namespace AttributeRouting.Specs.Subjects.Http
-{
- [RouteArea("ApiArea")]
- public class HttpAreasController : ApiController
- {
- [GET("Get")]
- public string Get()
- {
- return "";
- }
-
- [GET("ApiArea/DuplicatePrefix")]
- public string DuplicatePrefix()
- {
- return "";
- }
-
- [GET("ApiAreaAbsolute", IsAbsoluteUrl = true)]
- public string Absolute()
- {
- return "";
- }
-
- [GET("ApiAreas")]
- public string RouteBeginsWithAreaName()
- {
- return "";
- }
- }
-}
\ No newline at end of file
diff --git a/src/AttributeRouting.Specs/Subjects/Http/HttpExplicitAreaUrlController.cs b/src/AttributeRouting.Specs/Subjects/Http/HttpExplicitAreaUrlController.cs
deleted file mode 100644
index 37dac9c..0000000
--- a/src/AttributeRouting.Specs/Subjects/Http/HttpExplicitAreaUrlController.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System.Web.Http;
-using AttributeRouting.Web.Http;
-
-namespace AttributeRouting.Specs.Subjects.Http
-{
- [RouteArea("ApiArea", AreaUrl = "ApiExplicitArea")]
- public class HttpExplicitAreaUrlController : ApiController
- {
- [GET("Get")]
- public string Get()
- {
- return "";
- }
-
- [GET("ApiExplicitArea/DuplicatePrefix")]
- public string DuplicatePrefix()
- {
- return "";
- }
- }
-}
diff --git a/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs b/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs
index dced645..2c109cb 100644
--- a/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs
@@ -46,4 +46,14 @@ public ActionResult DuplicatePrefix()
return Content("");
}
}
+
+ [RouteArea]
+ public class DefaultRouteAreaController : Controller
+ {
+ [GET("Index")]
+ public ActionResult Index()
+ {
+ return Content("");
+ }
+ }
}
diff --git a/src/AttributeRouting/Framework/RouteReflector.cs b/src/AttributeRouting/Framework/RouteReflector.cs
index 91223f4..5176772 100644
--- a/src/AttributeRouting/Framework/RouteReflector.cs
+++ b/src/AttributeRouting/Framework/RouteReflector.cs
@@ -51,11 +51,16 @@ private IEnumerable BuildRouteSpecifications(IEnumerable(false)
let routeAreaAttribute = controllerType.GetCustomAttribute(true)
?? convention.SafeGet(x => x.GetDefaultRouteArea(controllerType))
+ let subdomain = GetAreaSubdomain(routeAreaAttribute)
+ let areaName = GetAreaName(routeAreaAttribute, controllerType)
+ let areaUrl = GetAreaUrl(routeAreaAttribute, subdomain, controllerType)
from actionMethod in controllerType.GetActionMethods(inheritActionsFromBaseController)
// NOTE: The oldConventionalRoutePrefix var is to support obsolete method.
// Once that method is removed, remove oldConventionalRoutePrefix from consideration,
@@ -67,8 +72,6 @@ from actionMethod in controllerType.GetActionMethods(inheritActionsFromBaseContr
: convention.SafeGet(x => x.GetDefaultRoutePrefix(controllerType)))
from routeAttribute in GetRouteAttributes(actionMethod, convention)
let routeName = routeAttribute.RouteName
- let subdomain = GetAreaSubdomain(routeAreaAttribute)
- let isAsyncController = controllerType.IsAsyncController()
let actionName = GetActionName(actionMethod, isAsyncController)
/* controlling precedence:
* site precedence of route >
@@ -82,8 +85,8 @@ from routeAttribute in GetRouteAttributes(actionMethod, convention)
orderby sitePrecedence, controllerIndex, controllerPrecedence, actionPrecedence
select new RouteSpecification
{
- AreaName = routeAreaAttribute.SafeGet(a => a.AreaName),
- AreaUrl = GetAreaUrl(routeAreaAttribute, subdomain),
+ AreaName = areaName,
+ AreaUrl = areaUrl,
AreaUrlTranslationKey = routeAreaAttribute.SafeGet(a => a.TranslationKey),
Subdomain = subdomain,
RoutePrefixUrl = routePrefixAttribute.SafeGet(p => p.Url),
@@ -138,13 +141,33 @@ private static IEnumerable GetRouteAttributes(MethodInfo action
return attributes;
}
+ ///
+ /// Gets the area name.
+ ///
+ /// The for the controller.
+ /// The type of the controller.
+ /// The name of the area.
+ private static string GetAreaName(RouteAreaAttribute routeAreaAttribute, Type controllerType)
+ {
+ if (routeAreaAttribute == null)
+ return null;
+
+ // If given an area name, then use it.
+ if (routeAreaAttribute.AreaName.HasValue())
+ return routeAreaAttribute.AreaName;
+
+ // Otherwise, use the last section of the namespace of the controller, as a convention.
+ return controllerType.GetLastSectionOfNamespace();
+ }
+
///
/// Gets the area URL prefix.
///
/// The for the controller.
/// The configured subdomain for the area.
+ /// The type of the controller.
/// The URL prefix for the area.
- private static string GetAreaUrl(RouteAreaAttribute routeAreaAttribute, string subdomain)
+ private static string GetAreaUrl(RouteAreaAttribute routeAreaAttribute, string subdomain, Type controllerType)
{
if (routeAreaAttribute == null)
return null;
@@ -156,7 +179,13 @@ private static string GetAreaUrl(RouteAreaAttribute routeAreaAttribute, string s
if (subdomain.HasValue() && routeAreaAttribute.AreaUrl.HasNoValue())
return null;
- return routeAreaAttribute.AreaUrl ?? routeAreaAttribute.AreaName;
+ // If we're given an area url or an area name, then use it.
+ var areaUrlOrName = routeAreaAttribute.AreaUrl ?? routeAreaAttribute.AreaName;
+ if (areaUrlOrName != null)
+ return areaUrlOrName;
+
+ // Otherwise, use the last section of the namespace of the controller, as a convention.
+ return controllerType.GetLastSectionOfNamespace();
}
///
diff --git a/src/AttributeRouting/Helpers/ObjectExtensions.cs b/src/AttributeRouting/Helpers/ObjectExtensions.cs
index 0ec02f0..dc8be6a 100644
--- a/src/AttributeRouting/Helpers/ObjectExtensions.cs
+++ b/src/AttributeRouting/Helpers/ObjectExtensions.cs
@@ -1,42 +1,42 @@
-using System;
-using System.Linq.Expressions;
-
-namespace AttributeRouting.Helpers
-{
- public static class ObjectExtensions
- {
+using System;
+using System.Linq.Expressions;
+
+namespace AttributeRouting.Helpers
+{
+ public static class ObjectExtensions
+ {
///
/// Returns true if the object is null or it's string representation is null or empty.
///
public static bool HasNoValue(this object obj)
{
return (obj == null || obj.ToString().HasNoValue());
- }
-
+ }
+
///
/// Will walk the given expression tree to get the value at the leaf.
/// If a NullReferenceException is thrown, the default for the leaf type will be returned.
///
- public static TResult SafeGet(this T obj, Expression> memberExpression)
- {
- return SafeGet(obj, memberExpression, default(TResult));
+ public static TResult SafeGet(this T obj, Expression> memberExpression)
+ {
+ return SafeGet(obj, memberExpression, default(TResult));
}
///
/// Will walk the given expression tree to get the value at the leaf.
/// If a NullReferenceException is thrown, the given default will be returned.
///
- public static TResult SafeGet(this T obj, Expression> memberExpression, TResult defaultValue)
- {
- try
- {
- var result = memberExpression.Compile().Invoke(obj);
- return result;
- }
- catch (NullReferenceException)
- {
- return defaultValue;
- }
- }
- }
-}
+ public static TResult SafeGet(this T obj, Expression> memberExpression, TResult defaultValue)
+ {
+ try
+ {
+ var result = memberExpression.Compile().Invoke(obj);
+ return result;
+ }
+ catch (NullReferenceException)
+ {
+ return defaultValue;
+ }
+ }
+ }
+}
diff --git a/src/AttributeRouting/Helpers/ReflectionExtensions.cs b/src/AttributeRouting/Helpers/ReflectionExtensions.cs
index 8b01e1e..c04e77f 100644
--- a/src/AttributeRouting/Helpers/ReflectionExtensions.cs
+++ b/src/AttributeRouting/Helpers/ReflectionExtensions.cs
@@ -8,6 +8,12 @@ namespace AttributeRouting.Helpers
{
public static class ReflectionExtensions
{
+ public static string GetLastSectionOfNamespace(this Type type)
+ {
+ var ns = type.Namespace;
+ return ns == null ? null : ns.Split('.').Last();
+ }
+
public static IEnumerable GetActionMethods(this Type type, bool inheritActionsFromBaseController)
{
var flags = BindingFlags.Public | BindingFlags.Instance;
diff --git a/src/AttributeRouting/RouteAreaAttribute.cs b/src/AttributeRouting/RouteAreaAttribute.cs
index b4b6cac..f837a88 100644
--- a/src/AttributeRouting/RouteAreaAttribute.cs
+++ b/src/AttributeRouting/RouteAreaAttribute.cs
@@ -8,11 +8,18 @@ namespace AttributeRouting
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class RouteAreaAttribute : Attribute
{
+ ///
+ /// Defines an area shared by all the routes defined in this controller.
+ /// The area name will be the most immediate section of the namespace for the controller.
+ ///
+ public RouteAreaAttribute() { }
+
///
/// Defines an area shared by all the routes defined in this controller.
///
/// The name of the containing area
public RouteAreaAttribute(string areaName)
+ : this()
{
if (areaName == null) throw new ArgumentNullException("areaName");
From dc4244ec274fc0bda37d491f6524d1465278fc94 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sat, 8 Dec 2012 18:12:23 -0700
Subject: [PATCH 12/97] #164 - added default ctor to RoutePrefixAttribute. This
default ctor will end up using the controller name as the route prefix for
routes in the controller.
---
.../Features/RoutePrefixes.feature | 12 +-
.../Features/RoutePrefixes.feature.cs | 29 +++-
.../Http/HttpRoutePrefixesController.cs | 10 ++
.../Subjects/RoutePrefixesControllers.cs | 127 ++++++++++--------
.../Framework/RouteReflector.cs | 34 +++--
src/AttributeRouting/RoutePrefixAttribute.cs | 7 +
6 files changed, 143 insertions(+), 76 deletions(-)
diff --git a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature
index 77c4e6b..f37cbae 100644
--- a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature
+++ b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature
@@ -78,4 +78,14 @@ Scenario: Generating routes when a route area and route prefix are defined and t
# Web API
Given I have registered the routes for the HttpAreaRoutePrefixesController
When I fetch the routes for the HttpAreaRoutePrefixes controller's RelativeUrlIsAreaUrl action
- Then the route url is "ApiArea/ApiPrefix/ApiArea"
\ No newline at end of file
+ Then the route url is "ApiArea/ApiPrefix/ApiArea"
+
+Scenario: Generating routes with the default ctor of the RoutePrefixAttribute
+ # MVC
+ Given I have registered the routes for the DefaultRoutePrefixController
+ When I fetch the routes for the DefaultRoutePrefix controller's Index action
+ Then the route url is "DefaultRoutePrefix/Index"
+ # Web API
+ Given I have registered the routes for the HttpDefaultRoutePrefixController
+ When I fetch the routes for the HttpDefaultRoutePrefix controller's Get action
+ Then the route url is "HttpDefaultRoutePrefix/Index"
diff --git a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs
index d5cc48d..d948adc 100644
--- a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs
@@ -1,9 +1,9 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.9.1.84
+// SpecFlow Version:1.9.0.77
// SpecFlow Generator Version:1.9.0.0
-// Runtime Version:4.0.30319.17929
+// Runtime Version:4.0.30319.18010
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.1.84")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Route Prefixes")]
@@ -251,6 +251,29 @@ public virtual void GeneratingRoutesWhenARouteAreaAndRoutePrefixAreDefinedAndThe
"l action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 81
testRunner.Then("the route url is \"ApiArea/ApiPrefix/ApiArea\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Generating routes with the default ctor of the RoutePrefixAttribute")]
+ public virtual void GeneratingRoutesWithTheDefaultCtorOfTheRoutePrefixAttribute()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes with the default ctor of the RoutePrefixAttribute", ((string[])(null)));
+#line 83
+this.ScenarioSetup(scenarioInfo);
+#line 85
+ testRunner.Given("I have registered the routes for the DefaultRoutePrefixController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 86
+ testRunner.When("I fetch the routes for the DefaultRoutePrefix controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 87
+ testRunner.Then("the route url is \"DefaultRoutePrefix/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 89
+ testRunner.Given("I have registered the routes for the HttpDefaultRoutePrefixController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 90
+ testRunner.When("I fetch the routes for the HttpDefaultRoutePrefix controller\'s Get action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 91
+ testRunner.Then("the route url is \"HttpDefaultRoutePrefix/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Subjects/Http/HttpRoutePrefixesController.cs b/src/AttributeRouting.Specs/Subjects/Http/HttpRoutePrefixesController.cs
index 652b96f..6236d88 100644
--- a/src/AttributeRouting.Specs/Subjects/Http/HttpRoutePrefixesController.cs
+++ b/src/AttributeRouting.Specs/Subjects/Http/HttpRoutePrefixesController.cs
@@ -30,4 +30,14 @@ public string RouteBeginsWithRoutePrefix()
return "";
}
}
+
+ [RoutePrefix]
+ public class HttpDefaultRoutePrefixController : ApiController
+ {
+ [GET("Index")]
+ public string Get()
+ {
+ return "";
+ }
+ }
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs b/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs
index 35ff040..8b6806b 100644
--- a/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs
@@ -1,63 +1,72 @@
-using System.Web.Mvc;
-using AttributeRouting.Web;
-using AttributeRouting.Web.Mvc;
-
-namespace AttributeRouting.Specs.Subjects
-{
- [RoutePrefix("Prefix")]
- public class RoutePrefixesController : Controller
- {
- [GET("Index")]
- public ActionResult Index()
- {
- return Content("");
- }
-
- [GET("Prefix/DuplicatePrefix")]
- public ActionResult DuplicatePrefix()
- {
- return Content("");
- }
-
- [GET("PrefixAbsolute", IsAbsoluteUrl = true)]
- public ActionResult Absolute()
- {
- return Content("");
- }
-
- [GET("Prefixer")]
- public ActionResult RouteBeginsWithRoutePrefix()
- {
- return Content("");
- }
- }
-
- [RouteArea("Area")]
- [RoutePrefix("Prefix")]
- public class AreaRoutePrefixesController : Controller
- {
- [GET("Index")]
- public ActionResult Index()
- {
- return Content("");
- }
-
- [GET("Prefix/DuplicatePrefix")]
- public ActionResult DuplicatePrefix()
- {
- return Content("");
- }
-
- [GET("AreaPrefixAbsolute", IsAbsoluteUrl = true)]
- public ActionResult Absolute()
- {
- return Content("");
- }
-
- [GET("Area")]
+using System.Web.Mvc;
+using AttributeRouting.Web.Mvc;
+
+namespace AttributeRouting.Specs.Subjects
+{
+ [RoutePrefix("Prefix")]
+ public class RoutePrefixesController : Controller
+ {
+ [GET("Index")]
+ public ActionResult Index()
+ {
+ return Content("");
+ }
+
+ [GET("Prefix/DuplicatePrefix")]
+ public ActionResult DuplicatePrefix()
+ {
+ return Content("");
+ }
+
+ [GET("PrefixAbsolute", IsAbsoluteUrl = true)]
+ public ActionResult Absolute()
+ {
+ return Content("");
+ }
+
+ [GET("Prefixer")]
+ public ActionResult RouteBeginsWithRoutePrefix()
+ {
+ return Content("");
+ }
+ }
+
+ [RouteArea("Area")]
+ [RoutePrefix("Prefix")]
+ public class AreaRoutePrefixesController : Controller
+ {
+ [GET("Index")]
+ public ActionResult Index()
+ {
+ return Content("");
+ }
+
+ [GET("Prefix/DuplicatePrefix")]
+ public ActionResult DuplicatePrefix()
+ {
+ return Content("");
+ }
+
+ [GET("AreaPrefixAbsolute", IsAbsoluteUrl = true)]
+ public ActionResult Absolute()
+ {
+ return Content("");
+ }
+
+ [GET("Area")]
public string RelativeUrlIsAreaUrl()
{
return "";
- }
- }
+ }
+ }
+
+ [RoutePrefix]
+ public class DefaultRoutePrefixController : Controller
+ {
+ [GET("Index")]
+ public ActionResult Index()
+ {
+ return Content("");
+ }
+ }
}
\ No newline at end of file
diff --git a/src/AttributeRouting/Framework/RouteReflector.cs b/src/AttributeRouting/Framework/RouteReflector.cs
index 5176772..c1a7182 100644
--- a/src/AttributeRouting/Framework/RouteReflector.cs
+++ b/src/AttributeRouting/Framework/RouteReflector.cs
@@ -70,6 +70,7 @@ from actionMethod in controllerType.GetActionMethods(inheritActionsFromBaseContr
?? (oldConventionalRoutePrefix.HasValue()
? new RoutePrefixAttribute(oldConventionalRoutePrefix)
: convention.SafeGet(x => x.GetDefaultRoutePrefix(controllerType)))
+ let routePrefixUrl = GetRoutePrefixUrl(routePrefixAttribute, controllerType)
from routeAttribute in GetRouteAttributes(actionMethod, convention)
let routeName = routeAttribute.RouteName
let actionName = GetActionName(actionMethod, isAsyncController)
@@ -89,7 +90,7 @@ from routeAttribute in GetRouteAttributes(actionMethod, convention)
AreaUrl = areaUrl,
AreaUrlTranslationKey = routeAreaAttribute.SafeGet(a => a.TranslationKey),
Subdomain = subdomain,
- RoutePrefixUrl = routePrefixAttribute.SafeGet(p => p.Url),
+ RoutePrefixUrl = routePrefixUrl,
RoutePrefixUrlTranslationKey = routePrefixAttribute.SafeGet(a => a.TranslationKey),
ControllerType = controllerType,
ControllerName = controllerType.GetControllerName(),
@@ -153,11 +154,8 @@ private static string GetAreaName(RouteAreaAttribute routeAreaAttribute, Type co
return null;
// If given an area name, then use it.
- if (routeAreaAttribute.AreaName.HasValue())
- return routeAreaAttribute.AreaName;
-
// Otherwise, use the last section of the namespace of the controller, as a convention.
- return controllerType.GetLastSectionOfNamespace();
+ return routeAreaAttribute.AreaName ?? controllerType.GetLastSectionOfNamespace();
}
///
@@ -180,12 +178,9 @@ private static string GetAreaUrl(RouteAreaAttribute routeAreaAttribute, string s
return null;
// If we're given an area url or an area name, then use it.
- var areaUrlOrName = routeAreaAttribute.AreaUrl ?? routeAreaAttribute.AreaName;
- if (areaUrlOrName != null)
- return areaUrlOrName;
-
// Otherwise, use the last section of the namespace of the controller, as a convention.
- return controllerType.GetLastSectionOfNamespace();
+ var areaUrlOrName = routeAreaAttribute.AreaUrl ?? routeAreaAttribute.AreaName;
+ return areaUrlOrName ?? controllerType.GetLastSectionOfNamespace();
}
///
@@ -203,10 +198,23 @@ private string GetAreaSubdomain(RouteAreaAttribute routeAreaAttribute)
where o.Key == routeAreaAttribute.AreaName
select o.Value).FirstOrDefault();
- if (subdomainOverride != null)
- return subdomainOverride;
+ return subdomainOverride ?? routeAreaAttribute.Subdomain;
+ }
+
+ ///
+ /// Gets the route prefix for the routes in the controller.
+ ///
+ /// The for the controller.
+ /// The type of the controller.
+ /// The URL prefix for the routes in the controller.
+ private static string GetRoutePrefixUrl(RoutePrefixAttribute routePrefixAttribute, Type controllerType)
+ {
+ if (routePrefixAttribute == null)
+ return null;
- return routeAreaAttribute.Subdomain;
+ // If we're given route prefix url, use it.
+ // Otherwise, use the controller name as a convention.
+ return routePrefixAttribute.Url ?? controllerType.GetControllerName();
}
///
diff --git a/src/AttributeRouting/RoutePrefixAttribute.cs b/src/AttributeRouting/RoutePrefixAttribute.cs
index b2238fe..78bb182 100644
--- a/src/AttributeRouting/RoutePrefixAttribute.cs
+++ b/src/AttributeRouting/RoutePrefixAttribute.cs
@@ -8,11 +8,18 @@ namespace AttributeRouting
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class RoutePrefixAttribute : Attribute
{
+ ///
+ /// Defines a prefix shared by all the routes defined in this controller.
+ /// The url prefix will be the name of the controller without the "Controller" suffix.
+ ///
+ public RoutePrefixAttribute() { }
+
///
/// Defines a prefix shared by all the routes defined in this controller.
///
/// The url prefix to apply to the routes
public RoutePrefixAttribute(string url)
+ : this()
{
if (url == null) throw new ArgumentNullException("url");
From a3049c170499d3ec7b4a41665d312bdb09409adf Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sat, 8 Dec 2012 20:03:48 -0700
Subject: [PATCH 13/97] #161 - fixed bug in url generation for routes that
included a querystring route param constraint.
---
.../AttributeRouting.Specs.csproj | 2 +-
.../Subjects/BugFixesController.cs | 16 -------
.../Subjects/BugFixesControllers.cs | 39 +++++++++++++++
.../Tests/BugFixTests.cs | 30 +++++++++---
.../Views/Home/Index.aspx | 48 +++++++++----------
.../Framework/HttpAttributeRoute.cs | 2 +-
.../Framework/AttributeRoute.cs | 3 +-
.../Framework/AttributeRouteExtensions.cs | 36 +++++++++++++-
8 files changed, 125 insertions(+), 51 deletions(-)
delete mode 100644 src/AttributeRouting.Specs/Subjects/BugFixesController.cs
create mode 100644 src/AttributeRouting.Specs/Subjects/BugFixesControllers.cs
diff --git a/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj b/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
index a163338..e502b37 100644
--- a/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
+++ b/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
@@ -139,7 +139,7 @@
-
+
diff --git a/src/AttributeRouting.Specs/Subjects/BugFixesController.cs b/src/AttributeRouting.Specs/Subjects/BugFixesController.cs
deleted file mode 100644
index 356bb3f..0000000
--- a/src/AttributeRouting.Specs/Subjects/BugFixesController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-using System.Web.Mvc;
-using AttributeRouting.Web.Mvc;
-
-namespace AttributeRouting.Specs.Subjects
-{
- [RoutePrefix("BugFixes")]
- public class BugFixesController : Controller
- {
- [GET("Gallery/_CenterImage/{guid_Gallery?}/{slideShow?}/{currentController?}/{image?}")]
- public ActionResult Issue43_OptionalParamsAreMucky(Guid? guid_Gallery, bool? slideShow, string currentController, string image)
- {
- return Content("I'm fixed!");
- }
- }
-}
diff --git a/src/AttributeRouting.Specs/Subjects/BugFixesControllers.cs b/src/AttributeRouting.Specs/Subjects/BugFixesControllers.cs
new file mode 100644
index 0000000..4e6940e
--- /dev/null
+++ b/src/AttributeRouting.Specs/Subjects/BugFixesControllers.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Web.Http;
+using System.Web.Mvc;
+using AttributeRouting.Web.Mvc;
+
+namespace AttributeRouting.Specs.Subjects
+{
+ [RoutePrefix("BugFixes")]
+ public class BugFixesController : Controller
+ {
+ [GET("Gallery/_CenterImage/{guid_Gallery?}/{slideShow?}/{currentController?}/{image?}")]
+ public ActionResult Issue43_OptionalParamsAreMucky(Guid? guid_Gallery, bool? slideShow, string currentController, string image)
+ {
+ return Content("I'm fixed!");
+ }
+ }
+
+ [RouteArea("Cms", AreaUrl = "{culture}/Cms")]
+ [RoutePrefix("Content")]
+ public class Issue161TestController : Controller
+ {
+ [GET("Items?{p:int}")]
+ public string Index(int p = 1)
+ {
+ return "";
+ }
+ }
+
+ [RouteArea("Cms", AreaUrl = "{culture}/Cms")]
+ [RoutePrefix("Content")]
+ public class Issue161TestHttpController : ApiController
+ {
+ [GET("Items?{p:int}", RouteName = "Issue161TestHttp")]
+ public string GetIndex(int p = 1)
+ {
+ return "";
+ }
+ }
+}
diff --git a/src/AttributeRouting.Specs/Tests/BugFixTests.cs b/src/AttributeRouting.Specs/Tests/BugFixTests.cs
index 339b158..6e5f728 100644
--- a/src/AttributeRouting.Specs/Tests/BugFixTests.cs
+++ b/src/AttributeRouting.Specs/Tests/BugFixTests.cs
@@ -3,9 +3,9 @@
using System.Linq;
using System.Threading;
using System.Web.Http;
+using System.Web.Mvc;
using System.Web.Routing;
using AttributeRouting.Framework.Localization;
-using AttributeRouting.Logging;
using AttributeRouting.Specs.Subjects;
using AttributeRouting.Specs.Subjects.Http;
using AttributeRouting.Web.Http.WebHost;
@@ -19,7 +19,23 @@ namespace AttributeRouting.Specs.Tests
public class BugFixTests
{
[Test]
- public void OData_style_http_url_bonks()
+ public void Issue161_Querystring_param_constraints_mucks_up_url_generation()
+ {
+ // re: issue #161
+
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config => config.AddRoutesFromController());
+
+ var urlHelper = new UrlHelper(MockBuilder.BuildRequestContext());
+ var routeValues = new { area = "Cms", culture = "en", p = 1 };
+ var expectedUrl = urlHelper.Action("Index", "Issue161Test", routeValues);
+
+ Assert.That(expectedUrl, Is.EqualTo("/en/Cms/Content/Items?p=1"));
+ }
+
+ [Test]
+ public void Issue120_OData_style_http_url_bonks()
{
// re: issue #120
@@ -32,7 +48,7 @@ public void OData_style_http_url_bonks()
}
[Test]
- public void Generating_two_routes_for_api_get_requests()
+ public void Issue102_Generating_two_routes_for_api_get_requests()
{
// re: issue #102
@@ -46,7 +62,7 @@ public void Generating_two_routes_for_api_get_requests()
}
[Test]
- public void Ensure_that_incompletely_mocked_request_context_does_not_generate_error_in_determining_http_method()
+ public void Issue25_Ensure_that_incompletely_mocked_request_context_does_not_generate_error_in_determining_http_method()
{
// re: issue #25
@@ -59,7 +75,7 @@ public void Ensure_that_incompletely_mocked_request_context_does_not_generate_er
}
[Test]
- public void Ensure_that_routes_with_optional_url_params_are_correctly_matched()
+ public void Issue43_Ensure_that_routes_with_optional_url_params_are_correctly_matched()
{
// re: issue #43
@@ -74,7 +90,7 @@ public void Ensure_that_routes_with_optional_url_params_are_correctly_matched()
}
[Test]
- public void Ensure_that_inbound_routing_works_when_contraining_by_culture()
+ public void Issue53_Ensure_that_inbound_routing_works_when_contraining_by_culture()
{
// re: issue #53
@@ -104,7 +120,7 @@ public void Ensure_that_inbound_routing_works_when_contraining_by_culture()
}
[Test]
- public void Ensure_that_async_controller_action_can_be_mapped()
+ public void Issue84_Ensure_that_async_controller_action_can_be_mapped()
{
// re: issue #84
RouteTable.Routes.Clear();
diff --git a/src/AttributeRouting.Tests.Web/Views/Home/Index.aspx b/src/AttributeRouting.Tests.Web/Views/Home/Index.aspx
index 8089916..ec4fa21 100644
--- a/src/AttributeRouting.Tests.Web/Views/Home/Index.aspx
+++ b/src/AttributeRouting.Tests.Web/Views/Home/Index.aspx
@@ -1,25 +1,25 @@
-<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
-
-
- Home
-
-
-
-
- Attribute-Based Routing for ASP.NET MVC
-
-
- The AttributeRouting documentation is available at
- github.
-
-
-
- Do you want to debug the routes for this sample?
-
-
+<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
+
+
+ Home
+
+
+
+
+ Attribute-Based Routing for ASP.NET MVC
+
- Just some test links:
- <%: Html.ActionLink("About", "About", "Home") %>
-
-
-
+ The AttributeRouting documentation is available at
+ github.
+
+
+
+ Do you want to debug the routes for this sample?
+
+
+ Just some test links:
+
+ - <%: Html.ActionLink("About", "About", "Home") %>
+
+
+
diff --git a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
index 09d3120..994dc8f 100644
--- a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
+++ b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
@@ -93,7 +93,7 @@ public override IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestM
public override IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary values)
{
// Let the underlying route do its thing, and if it does, then add some functionality on top.
- var virtualPathData = base.GetVirtualPath(request, values);
+ var virtualPathData = this.GetVirtualPath(() => base.GetVirtualPath(request, values));
if (virtualPathData == null)
return null;
diff --git a/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs b/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
index 92eba8b..24d7d9b 100644
--- a/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
+++ b/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
@@ -2,6 +2,7 @@
using System.Linq;
using System.Web;
using System.Web.Routing;
+using AttributeRouting.Constraints;
using AttributeRouting.Framework;
using AttributeRouting.Helpers;
@@ -91,7 +92,7 @@ public override RouteData GetRouteData(HttpContextBase httpContext)
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
// Let the underlying route do its thing, and if it does, then add some functionality on top.
- var virtualPathData = base.GetVirtualPath(requestContext, values);
+ var virtualPathData = this.GetVirtualPath(() => base.GetVirtualPath(requestContext, values));
if (virtualPathData == null)
return null;
diff --git a/src/AttributeRouting/Framework/AttributeRouteExtensions.cs b/src/AttributeRouting/Framework/AttributeRouteExtensions.cs
index 180c5de..7855fa5 100644
--- a/src/AttributeRouting/Framework/AttributeRouteExtensions.cs
+++ b/src/AttributeRouting/Framework/AttributeRouteExtensions.cs
@@ -1,7 +1,9 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
+using AttributeRouting.Constraints;
using AttributeRouting.Helpers;
namespace AttributeRouting.Framework
@@ -110,7 +112,7 @@ public static bool IsCultureNameMatched(this IAttributeRoute route, string curre
return true;
// Match if this route has no translations for the neutral current UI culture.
- if (!translations.Any(t => t.CultureName == currentUINeutralCultureName))
+ if (translations.All(t => t.CultureName != currentUINeutralCultureName))
return true;
}
@@ -118,6 +120,38 @@ public static bool IsCultureNameMatched(this IAttributeRoute route, string curre
return false;
}
+ public static TVirtualPathData GetVirtualPath(this IAttributeRoute route, Func fromBaseMethod)
+ where TVirtualPathData : class
+ {
+ // Remove querystring route constraints:
+ // the base GetVirtualPath will not inject route params that have constraints into the querystring.
+ var queryStringConstraints = new Dictionary();
+ var constraintKeys = route.Constraints.Keys.Select(k => k).ToList();
+ foreach (var constraintKey in constraintKeys)
+ {
+ var constraint = route.Constraints[constraintKey];
+ var constraintToTest = constraint is IOptionalRouteConstraintWrapper
+ ? ((IOptionalRouteConstraintWrapper)constraint).Constraint
+ : constraint;
+
+ if (constraintToTest is IQueryStringRouteConstraintWrapper)
+ queryStringConstraints.Add(constraintKey, constraint);
+
+ route.Constraints.Remove(constraintKey);
+ }
+
+ // Let the underlying route do its thing.
+ var virtualPathData = fromBaseMethod();
+
+ // Add the querystring constraints back in.
+ foreach (var queryStringConstraint in queryStringConstraints)
+ {
+ route.Constraints.Add(queryStringConstraint.Key, queryStringConstraint.Value);
+ }
+
+ return virtualPathData;
+ }
+
///
/// Gets the translated virtual path for this route.
///
From 7ab21d8b59d8e239ed3fe9ab42ed0f2bb3d7c888 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sat, 8 Dec 2012 21:44:01 -0700
Subject: [PATCH 14/97] #165 - added two flags to the route attributes:
IgnoreRoutePrefix and IgnoreAreaUrl. These flags control whether to prepend
route prefixes or area urls to the generated route url.
---
.../Features/RouteAreas.feature | 5 +
.../Features/RouteAreas.feature.cs | 47 +++++++---
.../Features/RoutePrefixes.feature | 10 ++
.../Features/RoutePrefixes.feature.cs | 93 ++++++++++++-------
.../Http/HttpRoutePrefixesController.cs | 6 ++
.../Subjects/RouteAreasControllers.cs | 6 ++
.../Subjects/RoutePrefixesControllers.cs | 6 ++
.../HttpRouteAttribute.cs | 10 +-
.../RouteAttribute.cs | 10 +-
.../Framework/RouteBuilder.cs | 45 ++++-----
.../Framework/RouteReflector.cs | 2 +
.../Framework/RouteSpecification.cs | 4 +
src/AttributeRouting/IRouteAttribute.cs | 12 +++
13 files changed, 179 insertions(+), 77 deletions(-)
diff --git a/src/AttributeRouting.Specs/Features/RouteAreas.feature b/src/AttributeRouting.Specs/Features/RouteAreas.feature
index 6294193..ace3daa 100644
--- a/src/AttributeRouting.Specs/Features/RouteAreas.feature
+++ b/src/AttributeRouting.Specs/Features/RouteAreas.feature
@@ -21,6 +21,11 @@ Scenario: Generating area routes when route url starts with the area prefix
When I fetch the routes for the Areas controller's RouteBeginsWithAreaName action
Then the route url is "Area/Areas"
+Scenario: Generating area routes when ignoring the area url
+ Given I have registered the routes for the AreasController
+ When I fetch the routes for the Areas controller's NoAreaUrl action
+ Then the route url is "NoAreaUrl"
+
Scenario: Generating area routes with an explicit area url
Given I have registered the routes for the ExplicitAreaUrlController
When I fetch the routes for the ExplicitAreaUrl controller's Index action
diff --git a/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs b/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs
index 2d1d851..51449c0 100644
--- a/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs
@@ -134,20 +134,37 @@ public virtual void GeneratingAreaRoutesWhenRouteUrlStartsWithTheAreaPrefix()
this.ScenarioCleanup();
}
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Generating area routes when ignoring the area url")]
+ public virtual void GeneratingAreaRoutesWhenIgnoringTheAreaUrl()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating area routes when ignoring the area url", ((string[])(null)));
+#line 24
+this.ScenarioSetup(scenarioInfo);
+#line 25
+ testRunner.Given("I have registered the routes for the AreasController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 26
+ testRunner.When("I fetch the routes for the Areas controller\'s NoAreaUrl action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 27
+ testRunner.Then("the route url is \"NoAreaUrl\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Generating area routes with an explicit area url")]
public virtual void GeneratingAreaRoutesWithAnExplicitAreaUrl()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating area routes with an explicit area url", ((string[])(null)));
-#line 24
+#line 29
this.ScenarioSetup(scenarioInfo);
-#line 25
+#line 30
testRunner.Given("I have registered the routes for the ExplicitAreaUrlController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 26
+#line 31
testRunner.When("I fetch the routes for the ExplicitAreaUrl controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 27
+#line 32
testRunner.Then("the route url is \"ExplicitArea/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 28
+#line 33
testRunner.And("the data token for \"area\" is \"Area\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
@@ -160,15 +177,15 @@ public virtual void GeneratingAreaRoutesWithAnExplicitAreaUrlWhenRouteUrlsSpecif
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating area routes with an explicit area url when route urls specify a duplic" +
"ate area prefix", ((string[])(null)));
-#line 30
+#line 35
this.ScenarioSetup(scenarioInfo);
-#line 31
+#line 36
testRunner.Given("I have registered the routes for the ExplicitAreaUrlController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 32
+#line 37
testRunner.When("I fetch the routes for the ExplicitAreaUrl controller\'s DuplicatePrefix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 33
+#line 38
testRunner.Then("the route url is \"ExplicitArea/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 34
+#line 39
testRunner.And("the data token for \"area\" is \"Area\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
@@ -179,15 +196,15 @@ public virtual void GeneratingAreaRoutesWithAnExplicitAreaUrlWhenRouteUrlsSpecif
public virtual void GeneratingAreaRoutesWithTheDefaultCtorOfTheRouteAreaAttribute()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating area routes with the default ctor of the RouteAreaAttribute", ((string[])(null)));
-#line 36
+#line 41
this.ScenarioSetup(scenarioInfo);
-#line 37
+#line 42
testRunner.Given("I have registered the routes for the DefaultRouteAreaController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 38
+#line 43
testRunner.When("I fetch the routes for the DefaultRouteArea controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 39
+#line 44
testRunner.Then("the route url is \"Subjects/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 40
+#line 45
testRunner.And("the data token for \"area\" is \"Subjects\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
diff --git a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature
index f37cbae..0bc4ffe 100644
--- a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature
+++ b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature
@@ -40,6 +40,16 @@ Scenario: Generating prefixed routes when route url starts with the route prefix
When I fetch the routes for the HttpRoutePrefixes controller's RouteBeginsWithRoutePrefix action
Then the route url is "ApiPrefix/ApiPrefixer"
+Scenario: Generating prefixed routes when ignoring the route prefix
+ # MVC
+ Given I have registered the routes for the RoutePrefixesController
+ When I fetch the routes for the RoutePrefixes controller's NoPrefix action
+ Then the route url is "NoPrefix"
+ # Web API
+ Given I have registered the routes for the HttpRoutePrefixesController
+ When I fetch the routes for the HttpRoutePrefixes controller's NoPrefix action
+ Then the route url is "NoApiPrefix"
+
Scenario: Generating prefixed area routes
# MVC
Given I have registered the routes for the AreaRoutePrefixesController
diff --git a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs
index d948adc..03fc662 100644
--- a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs
@@ -158,24 +158,47 @@ public virtual void GeneratingPrefixedRoutesWhenRouteUrlStartsWithTheRoutePrefix
this.ScenarioCleanup();
}
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Generating prefixed routes when ignoring the route prefix")]
+ public virtual void GeneratingPrefixedRoutesWhenIgnoringTheRoutePrefix()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating prefixed routes when ignoring the route prefix", ((string[])(null)));
+#line 43
+this.ScenarioSetup(scenarioInfo);
+#line 45
+ testRunner.Given("I have registered the routes for the RoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 46
+ testRunner.When("I fetch the routes for the RoutePrefixes controller\'s NoPrefix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 47
+ testRunner.Then("the route url is \"NoPrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 49
+ testRunner.Given("I have registered the routes for the HttpRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 50
+ testRunner.When("I fetch the routes for the HttpRoutePrefixes controller\'s NoPrefix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 51
+ testRunner.Then("the route url is \"NoApiPrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Generating prefixed area routes")]
public virtual void GeneratingPrefixedAreaRoutes()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating prefixed area routes", ((string[])(null)));
-#line 43
+#line 53
this.ScenarioSetup(scenarioInfo);
-#line 45
+#line 55
testRunner.Given("I have registered the routes for the AreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 46
+#line 56
testRunner.When("I fetch the routes for the AreaRoutePrefixes controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 47
+#line 57
testRunner.Then("the route url is \"Area/Prefix/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 49
+#line 59
testRunner.Given("I have registered the routes for the HttpAreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 50
+#line 60
testRunner.When("I fetch the routes for the HttpAreaRoutePrefixes controller\'s Get action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 51
+#line 61
testRunner.Then("the route url is \"ApiArea/ApiPrefix/Get\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
@@ -186,20 +209,20 @@ public virtual void GeneratingPrefixedAreaRoutes()
public virtual void GeneratingPrefixedAreaRoutesWhenRouteUrlsSpecifyADuplicatePrefix()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating prefixed area routes when route urls specify a duplicate prefix", ((string[])(null)));
-#line 53
+#line 63
this.ScenarioSetup(scenarioInfo);
-#line 55
+#line 65
testRunner.Given("I have registered the routes for the AreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 56
+#line 66
testRunner.When("I fetch the routes for the AreaRoutePrefixes controller\'s DuplicatePrefix action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 57
+#line 67
testRunner.Then("the route url is \"Area/Prefix/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 59
+#line 69
testRunner.Given("I have registered the routes for the HttpAreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 60
+#line 70
testRunner.When("I fetch the routes for the HttpAreaRoutePrefixes controller\'s DuplicatePrefix act" +
"ion", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 61
+#line 71
testRunner.Then("the route url is \"ApiArea/ApiPrefix/DuplicatePrefix\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
@@ -210,19 +233,19 @@ public virtual void GeneratingPrefixedAreaRoutesWhenRouteUrlsSpecifyADuplicatePr
public virtual void GeneratingAbsoluteRoutesWhenARouteAreaAndRoutePrefixIsDefined()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating absolute routes when a route area and route prefix is defined", ((string[])(null)));
-#line 63
+#line 73
this.ScenarioSetup(scenarioInfo);
-#line 65
+#line 75
testRunner.Given("I have registered the routes for the AreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 66
+#line 76
testRunner.When("I fetch the routes for the AreaRoutePrefixes controller\'s Absolute action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 67
+#line 77
testRunner.Then("the route url is \"AreaPrefixAbsolute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 69
+#line 79
testRunner.Given("I have registered the routes for the HttpAreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 70
+#line 80
testRunner.When("I fetch the routes for the HttpAreaRoutePrefixes controller\'s Absolute action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 71
+#line 81
testRunner.Then("the route url is \"ApiAreaPrefixAbsolute\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
@@ -235,21 +258,21 @@ public virtual void GeneratingRoutesWhenARouteAreaAndRoutePrefixAreDefinedAndThe
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes when a route area and route prefix are defined and the action r" +
"especifies the area url", ((string[])(null)));
-#line 73
+#line 83
this.ScenarioSetup(scenarioInfo);
-#line 75
+#line 85
testRunner.Given("I have registered the routes for the AreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 76
+#line 86
testRunner.When("I fetch the routes for the AreaRoutePrefixes controller\'s RelativeUrlIsAreaUrl ac" +
"tion", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 77
+#line 87
testRunner.Then("the route url is \"Area/Prefix/Area\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 79
+#line 89
testRunner.Given("I have registered the routes for the HttpAreaRoutePrefixesController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 80
+#line 90
testRunner.When("I fetch the routes for the HttpAreaRoutePrefixes controller\'s RelativeUrlIsAreaUr" +
"l action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 81
+#line 91
testRunner.Then("the route url is \"ApiArea/ApiPrefix/ApiArea\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
@@ -260,19 +283,19 @@ public virtual void GeneratingRoutesWhenARouteAreaAndRoutePrefixAreDefinedAndThe
public virtual void GeneratingRoutesWithTheDefaultCtorOfTheRoutePrefixAttribute()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes with the default ctor of the RoutePrefixAttribute", ((string[])(null)));
-#line 83
+#line 93
this.ScenarioSetup(scenarioInfo);
-#line 85
+#line 95
testRunner.Given("I have registered the routes for the DefaultRoutePrefixController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 86
+#line 96
testRunner.When("I fetch the routes for the DefaultRoutePrefix controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 87
+#line 97
testRunner.Then("the route url is \"DefaultRoutePrefix/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 89
+#line 99
testRunner.Given("I have registered the routes for the HttpDefaultRoutePrefixController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 90
+#line 100
testRunner.When("I fetch the routes for the HttpDefaultRoutePrefix controller\'s Get action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 91
+#line 101
testRunner.Then("the route url is \"HttpDefaultRoutePrefix/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
diff --git a/src/AttributeRouting.Specs/Subjects/Http/HttpRoutePrefixesController.cs b/src/AttributeRouting.Specs/Subjects/Http/HttpRoutePrefixesController.cs
index 6236d88..597ace7 100644
--- a/src/AttributeRouting.Specs/Subjects/Http/HttpRoutePrefixesController.cs
+++ b/src/AttributeRouting.Specs/Subjects/Http/HttpRoutePrefixesController.cs
@@ -29,6 +29,12 @@ public string RouteBeginsWithRoutePrefix()
{
return "";
}
+
+ [GET("NoApiPrefix", IgnoreRoutePrefix = true)]
+ public string NoPrefix()
+ {
+ return "";
+ }
}
[RoutePrefix]
diff --git a/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs b/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs
index 2c109cb..e9228cf 100644
--- a/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs
@@ -29,6 +29,12 @@ public ActionResult RouteBeginsWithAreaName()
{
return Content("");
}
+
+ [GET("NoAreaUrl", IgnoreAreaUrl = true)]
+ public ActionResult NoAreaUrl()
+ {
+ return Content("");
+ }
}
[RouteArea("Area", AreaUrl = "ExplicitArea")]
diff --git a/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs b/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs
index 8b6806b..0297b62 100644
--- a/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs
@@ -29,6 +29,12 @@ public ActionResult RouteBeginsWithRoutePrefix()
{
return Content("");
}
+
+ [GET("NoPrefix", IgnoreRoutePrefix = true)]
+ public string NoPrefix()
+ {
+ return "";
+ }
}
[RouteArea("Area")]
diff --git a/src/AttributeRouting.Web.Http/HttpRouteAttribute.cs b/src/AttributeRouting.Web.Http/HttpRouteAttribute.cs
index 9b7f627..6a7053d 100644
--- a/src/AttributeRouting.Web.Http/HttpRouteAttribute.cs
+++ b/src/AttributeRouting.Web.Http/HttpRouteAttribute.cs
@@ -62,7 +62,11 @@ public int Precedence
public string RouteName { get; set; }
- public bool IsAbsoluteUrl { get; set; }
+ public bool IsAbsoluteUrl
+ {
+ get { return IgnoreAreaUrl && IgnoreRoutePrefix; }
+ set { IgnoreAreaUrl = IgnoreRoutePrefix = value; }
+ }
public string TranslationKey { get; set; }
@@ -89,5 +93,9 @@ public bool AppendTrailingSlash
}
public bool? AppendTrailingSlashFlag { get; private set; }
+
+ public bool IgnoreRoutePrefix { get; set; }
+
+ public bool IgnoreAreaUrl { get; set; }
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Mvc/RouteAttribute.cs b/src/AttributeRouting.Web.Mvc/RouteAttribute.cs
index cf37cda..a5b0523 100644
--- a/src/AttributeRouting.Web.Mvc/RouteAttribute.cs
+++ b/src/AttributeRouting.Web.Mvc/RouteAttribute.cs
@@ -84,7 +84,11 @@ public int Precedence
public string RouteName { get; set; }
- public bool IsAbsoluteUrl { get; set; }
+ public bool IsAbsoluteUrl
+ {
+ get { return IgnoreAreaUrl && IgnoreRoutePrefix; }
+ set { IgnoreAreaUrl = IgnoreRoutePrefix = value; }
+ }
public string TranslationKey { get; set; }
@@ -111,6 +115,10 @@ public bool AppendTrailingSlash
}
public bool? AppendTrailingSlashFlag { get; private set; }
+
+ public bool IgnoreRoutePrefix { get; set; }
+
+ public bool IgnoreAreaUrl { get; set; }
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index aa05499..35fd328 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -103,13 +103,12 @@ private string CreateRouteUrl(RouteSpecification routeSpec)
return CreateRouteUrl(routeSpec.RouteUrl,
routeSpec.RoutePrefixUrl,
routeSpec.AreaUrl,
- routeSpec.IsAbsoluteUrl,
- routeSpec.UseLowercaseRoute);
+ routeSpec);
}
- private string CreateRouteUrl(string routeUrl, string routePrefix, string areaUrl, bool isAbsoluteUrl, bool? useLowercaseRoute)
+ private string CreateRouteUrl(string routeUrl, string routePrefix, string areaUrl, RouteSpecification routeSpec)
{
- var tokenizedUrl = BuildTokenizedUrl(routeUrl, routePrefix, areaUrl, isAbsoluteUrl);
+ var tokenizedUrl = BuildTokenizedUrl(routeUrl, routePrefix, areaUrl, routeSpec);
var tokenizedPath = RemoveQueryString(tokenizedUrl);
var detokenizedPath = DetokenizeUrl(tokenizedPath);
@@ -129,7 +128,7 @@ private string CreateRouteUrl(string routeUrl, string routePrefix, string areaUr
var urlBuilder = new StringBuilder(detokenizedPath);
// If we are lowercasing routes, then lowercase everything but the route params
- var lower = useLowercaseRoute.HasValue ? useLowercaseRoute.Value : _configuration.UseLowercaseRoutes;
+ var lower = routeSpec.UseLowercaseRoute.GetValueOrDefault(_configuration.UseLowercaseRoutes);
if (lower)
{
for (var i = 0; i < urlBuilder.Length; i++)
@@ -202,7 +201,7 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
constraints.Add("httpMethod", _routeConstraintFactory.CreateRestfulHttpMethodConstraint(routeSpec.HttpMethods));
// Work from a complete, tokenized url; ie: support constraints in area urls, route prefix urls, and route urls.
- var tokenizedUrl = BuildTokenizedUrl(routeSpec.RouteUrl, routeSpec.RoutePrefixUrl, routeSpec.AreaUrl, routeSpec.IsAbsoluteUrl);
+ var tokenizedUrl = BuildTokenizedUrl(routeSpec.RouteUrl, routeSpec.RoutePrefixUrl, routeSpec.AreaUrl, routeSpec);
var urlParameters = GetUrlParameterContents(tokenizedUrl).ToList();
// Need to keep track of query params.
@@ -320,27 +319,24 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
return constraints;
}
- private string BuildTokenizedUrl(string routeUrl, string routePrefixUrl, string areaUrl, bool isAbsoluteUrl)
+ private string BuildTokenizedUrl(string routeUrl, string routePrefixUrl, string areaUrl, RouteSpecification routeSpec)
{
var delimitedUrl = routeUrl + "/";
-
- if (!isAbsoluteUrl)
+
+ // Prepend prefix if available
+ if (routePrefixUrl.HasValue() && !routeSpec.IgnoreRoutePrefix)
{
- // Prepend prefix if available
- if (routePrefixUrl.HasValue())
- {
- var delimitedRoutePrefix = routePrefixUrl + "/";
- if (!delimitedUrl.StartsWith(delimitedRoutePrefix))
- delimitedUrl = delimitedRoutePrefix + delimitedUrl;
- }
+ var delimitedRoutePrefix = routePrefixUrl + "/";
+ if (!delimitedUrl.StartsWith(delimitedRoutePrefix))
+ delimitedUrl = delimitedRoutePrefix + delimitedUrl;
+ }
- // Prepend area url if available
- if (areaUrl.HasValue())
- {
- var delimitedAreaUrl = areaUrl + "/";
- if (!delimitedUrl.StartsWith(delimitedAreaUrl))
- delimitedUrl = delimitedAreaUrl + delimitedUrl;
- }
+ // Prepend area url if available
+ if (areaUrl.HasValue() && !routeSpec.IgnoreAreaUrl)
+ {
+ var delimitedAreaUrl = areaUrl + "/";
+ if (!delimitedUrl.StartsWith(delimitedAreaUrl))
+ delimitedUrl = delimitedAreaUrl + delimitedUrl;
}
return delimitedUrl.Trim('/');
@@ -433,8 +429,7 @@ private IEnumerable CreateRouteTranslations(RouteSpecification
var routeUrl = CreateRouteUrl(translatedRouteUrl ?? routeSpec.RouteUrl,
translatedRoutePrefix ?? routeSpec.RoutePrefixUrl,
translatedAreaUrl ?? routeSpec.AreaUrl,
- routeSpec.IsAbsoluteUrl,
- routeSpec.UseLowercaseRoute);
+ routeSpec);
var translatedRoutes = _routeFactory.CreateAttributeRoutes(routeUrl,
CreateRouteDefaults(routeSpec),
diff --git a/src/AttributeRouting/Framework/RouteReflector.cs b/src/AttributeRouting/Framework/RouteReflector.cs
index c1a7182..fa88a67 100644
--- a/src/AttributeRouting/Framework/RouteReflector.cs
+++ b/src/AttributeRouting/Framework/RouteReflector.cs
@@ -100,6 +100,8 @@ from routeAttribute in GetRouteAttributes(actionMethod, convention)
HttpMethods = routeAttribute.HttpMethods,
RouteName = routeName,
IsAbsoluteUrl = routeAttribute.IsAbsoluteUrl,
+ IgnoreRoutePrefix = routeAttribute.IgnoreRoutePrefix,
+ IgnoreAreaUrl = routeAttribute.IgnoreAreaUrl,
UseLowercaseRoute = routeAttribute.UseLowercaseRouteFlag,
PreserveCaseForUrlParameters = routeAttribute.PreserveCaseForUrlParametersFlag,
AppendTrailingSlash = routeAttribute.AppendTrailingSlashFlag
diff --git a/src/AttributeRouting/Framework/RouteSpecification.cs b/src/AttributeRouting/Framework/RouteSpecification.cs
index 3b8cab4..923ff0e 100644
--- a/src/AttributeRouting/Framework/RouteSpecification.cs
+++ b/src/AttributeRouting/Framework/RouteSpecification.cs
@@ -40,5 +40,9 @@ public class RouteSpecification
public bool? PreserveCaseForUrlParameters { get; set; }
public bool? AppendTrailingSlash { get; set; }
+
+ public bool IgnoreRoutePrefix { get; set; }
+
+ public bool IgnoreAreaUrl { get; set; }
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting/IRouteAttribute.cs b/src/AttributeRouting/IRouteAttribute.cs
index f3795ce..d35de64 100644
--- a/src/AttributeRouting/IRouteAttribute.cs
+++ b/src/AttributeRouting/IRouteAttribute.cs
@@ -109,5 +109,17 @@ public interface IRouteAttribute
/// Gets the tri-state value for AppendTrailingSlash.
///
bool? AppendTrailingSlashFlag { get; }
+
+ ///
+ /// If true, will ignore any route prefix specified via the
+ /// when building up the route URL.
+ ///
+ bool IgnoreRoutePrefix { get; set; }
+
+ ///
+ /// If true, will ignore any area URL prefix specified via the
+ /// when building up the route URL.
+ ///
+ bool IgnoreAreaUrl { get; set; }
}
}
\ No newline at end of file
From 3c3512e0c92967a2489f3974cd063c315cdc705f Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sun, 9 Dec 2012 00:13:52 -0700
Subject: [PATCH 15/97] #155 - added ability to specify multiple route prefixes
on a controller, which will result in all the routes for the actions being
prefixed by each.
---
.../Features/RoutePrefixes.feature | 12 ++
.../Features/RoutePrefixes.feature.cs | 27 +++
.../Http/HttpRoutePrefixesController.cs | 11 ++
.../Subjects/RoutePrefixesControllers.cs | 12 ++
.../DefaultHttpRouteConventionAttribute.cs | 4 +-
.../RestfulRouteConventionAttribute.cs | 4 +-
.../Framework/RouteReflector.cs | 186 +++++++++++++-----
.../Framework/RouteSpecification.cs | 10 +
.../RouteConventionAttributeBase.cs | 4 +-
src/AttributeRouting/RoutePrefixAttribute.cs | 17 +-
10 files changed, 225 insertions(+), 62 deletions(-)
diff --git a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature
index 0bc4ffe..c7f904e 100644
--- a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature
+++ b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature
@@ -99,3 +99,15 @@ Scenario: Generating routes with the default ctor of the RoutePrefixAttribute
Given I have registered the routes for the HttpDefaultRoutePrefixController
When I fetch the routes for the HttpDefaultRoutePrefix controller's Get action
Then the route url is "HttpDefaultRoutePrefix/Index"
+
+Scenario: Generating prefixed routes when specifying multiple route prefixes
+ # MVC
+ Given I have registered the routes for the MultipleRoutePrefixController
+ When I fetch the routes for the MultipleRoutePrefix controller's Index action
+ Then the 1st route url is "FirstPrefix/Index"
+ And the 3rd route url is "SecondPrefix/Index"
+ # Web API
+ Given I have registered the routes for the HttpMultipleRoutePrefixController
+ When I fetch the routes for the HttpMultipleRoutePrefix controller's Get action
+ Then the 1st route url is "HttpFirstPrefix/Index"
+ And the 2st route url is "HttpSecondPrefix/Index"
diff --git a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs
index 03fc662..8af73cb 100644
--- a/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RoutePrefixes.feature.cs
@@ -297,6 +297,33 @@ public virtual void GeneratingRoutesWithTheDefaultCtorOfTheRoutePrefixAttribute(
testRunner.When("I fetch the routes for the HttpDefaultRoutePrefix controller\'s Get action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 101
testRunner.Then("the route url is \"HttpDefaultRoutePrefix/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Generating prefixed routes when specifying multiple route prefixes")]
+ public virtual void GeneratingPrefixedRoutesWhenSpecifyingMultipleRoutePrefixes()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating prefixed routes when specifying multiple route prefixes", ((string[])(null)));
+#line 103
+this.ScenarioSetup(scenarioInfo);
+#line 105
+ testRunner.Given("I have registered the routes for the MultipleRoutePrefixController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 106
+ testRunner.When("I fetch the routes for the MultipleRoutePrefix controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 107
+ testRunner.Then("the 1st route url is \"FirstPrefix/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 108
+ testRunner.And("the 3rd route url is \"SecondPrefix/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 110
+ testRunner.Given("I have registered the routes for the HttpMultipleRoutePrefixController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 111
+ testRunner.When("I fetch the routes for the HttpMultipleRoutePrefix controller\'s Get action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 112
+ testRunner.Then("the 1st route url is \"HttpFirstPrefix/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 113
+ testRunner.And("the 2st route url is \"HttpSecondPrefix/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Subjects/Http/HttpRoutePrefixesController.cs b/src/AttributeRouting.Specs/Subjects/Http/HttpRoutePrefixesController.cs
index 597ace7..3f99a2e 100644
--- a/src/AttributeRouting.Specs/Subjects/Http/HttpRoutePrefixesController.cs
+++ b/src/AttributeRouting.Specs/Subjects/Http/HttpRoutePrefixesController.cs
@@ -46,4 +46,15 @@ public string Get()
return "";
}
}
+
+ [RoutePrefix("HttpFirstPrefix", Precedence = 1)]
+ [RoutePrefix("HttpSecondPrefix")]
+ public class HttpMultipleRoutePrefixController : ApiController
+ {
+ [GET("Index")]
+ public string Get()
+ {
+ return "";
+ }
+ }
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs b/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs
index 0297b62..92c673a 100644
--- a/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs
@@ -75,4 +75,16 @@ public ActionResult Index()
return Content("");
}
}
+
+ [RoutePrefix("FirstPrefix", Precedence = 1)]
+ [RoutePrefix("SecondPrefix")]
+ public class MultipleRoutePrefixController : Controller
+ {
+ [GET("Index")]
+ [GET("This/Is/Absolute", IsAbsoluteUrl = true)]
+ public ActionResult Index()
+ {
+ return Content("");
+ }
+ }
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http/DefaultHttpRouteConventionAttribute.cs b/src/AttributeRouting.Web.Http/DefaultHttpRouteConventionAttribute.cs
index adf29f5..7e04104 100644
--- a/src/AttributeRouting.Web.Http/DefaultHttpRouteConventionAttribute.cs
+++ b/src/AttributeRouting.Web.Http/DefaultHttpRouteConventionAttribute.cs
@@ -67,9 +67,9 @@ public override IEnumerable GetRouteAttributes(MethodInfo actio
}
}
- public override RoutePrefixAttribute GetDefaultRoutePrefix(Type controllerType)
+ public override IEnumerable GetDefaultRoutePrefixes(Type controllerType)
{
- return new RoutePrefixAttribute(controllerType.GetControllerName());
+ yield return new RoutePrefixAttribute(controllerType.GetControllerName());
}
private IRouteAttribute BuildRouteAttribute(HttpRouteConventionInfo convention)
diff --git a/src/AttributeRouting.Web.Mvc/RestfulRouteConventionAttribute.cs b/src/AttributeRouting.Web.Mvc/RestfulRouteConventionAttribute.cs
index f0699c7..4ef35b1 100644
--- a/src/AttributeRouting.Web.Mvc/RestfulRouteConventionAttribute.cs
+++ b/src/AttributeRouting.Web.Mvc/RestfulRouteConventionAttribute.cs
@@ -33,9 +33,9 @@ public override IEnumerable GetRouteAttributes(MethodInfo actio
yield return BuildRouteAttribute(convention);
}
- public override RoutePrefixAttribute GetDefaultRoutePrefix(Type controllerType)
+ public override IEnumerable GetDefaultRoutePrefixes(Type controllerType)
{
- return new RoutePrefixAttribute(controllerType.GetControllerName());
+ yield return new RoutePrefixAttribute(controllerType.GetControllerName());
}
private IRouteAttribute BuildRouteAttribute(RestfulRouteConventionInfo convention)
diff --git a/src/AttributeRouting/Framework/RouteReflector.cs b/src/AttributeRouting/Framework/RouteReflector.cs
index fa88a67..ae3ddd3 100644
--- a/src/AttributeRouting/Framework/RouteReflector.cs
+++ b/src/AttributeRouting/Framework/RouteReflector.cs
@@ -50,62 +50,99 @@ private IEnumerable BuildRouteSpecifications(IEnumerable();
- // TODO: Reorganize this rat's nest! :)
- return (from controllerType in controllerTypes
- let controllerIndex = controllerCount++
- let isAsyncController = controllerType.IsAsyncController()
- let convention = controllerType.GetCustomAttribute(false)
- let routeAreaAttribute = controllerType.GetCustomAttribute(true)
- ?? convention.SafeGet(x => x.GetDefaultRouteArea(controllerType))
- let subdomain = GetAreaSubdomain(routeAreaAttribute)
- let areaName = GetAreaName(routeAreaAttribute, controllerType)
- let areaUrl = GetAreaUrl(routeAreaAttribute, subdomain, controllerType)
- from actionMethod in controllerType.GetActionMethods(inheritActionsFromBaseController)
- // NOTE: The oldConventionalRoutePrefix var is to support obsolete method.
- // Once that method is removed, remove oldConventionalRoutePrefix from consideration,
- // and move the let routePrefixAttribute op above the loop inside the actionMethed.
- let oldConventionalRoutePrefix = convention.SafeGet(x => x.GetDefaultRoutePrefix(actionMethod))
- let routePrefixAttribute = controllerType.GetCustomAttribute(true)
- ?? (oldConventionalRoutePrefix.HasValue()
- ? new RoutePrefixAttribute(oldConventionalRoutePrefix)
- : convention.SafeGet(x => x.GetDefaultRoutePrefix(controllerType)))
- let routePrefixUrl = GetRoutePrefixUrl(routePrefixAttribute, controllerType)
- from routeAttribute in GetRouteAttributes(actionMethod, convention)
- let routeName = routeAttribute.RouteName
- let actionName = GetActionName(actionMethod, isAsyncController)
- /* controlling precedence:
- * site precedence of route >
- * controller index >
- * controller precedence of route >
- * action precedence of route
- */
- let sitePrecedence = GetSortableOrder(routeAttribute.SitePrecedence)
- let controllerPrecedence = GetSortableOrder(routeAttribute.ControllerPrecedence)
- let actionPrecedence = GetSortableOrder(routeAttribute.ActionPrecedence)
- orderby sitePrecedence, controllerIndex, controllerPrecedence, actionPrecedence
- select new RouteSpecification
+ // For each controller type:
+ foreach (var controllerType in controllerTypes)
+ {
+ var controllerIndex = controllerCount++;
+ var convention = controllerType.GetCustomAttribute(false);
+ var routeAreaAttribute = GetRouteAreaAttribute(controllerType, convention);
+
+ // For each action method on the controller:
+ var actionMethods = controllerType.GetActionMethods(inheritActionsFromBaseController);
+ foreach (var actionMethod in actionMethods)
+ {
+ var routePrefixAttributes = GetRoutePrefixAttributes(controllerType, convention, actionMethod).ToList();
+
+ // For each route attribute on the action method:
+ var routeAttributes = GetRouteAttributes(actionMethod, convention);
+ foreach (var routeAttribute in routeAttributes)
{
- AreaName = areaName,
- AreaUrl = areaUrl,
- AreaUrlTranslationKey = routeAreaAttribute.SafeGet(a => a.TranslationKey),
- Subdomain = subdomain,
- RoutePrefixUrl = routePrefixUrl,
- RoutePrefixUrlTranslationKey = routePrefixAttribute.SafeGet(a => a.TranslationKey),
- ControllerType = controllerType,
- ControllerName = controllerType.GetControllerName(),
- ActionName = actionName,
- RouteUrl = routeAttribute.RouteUrl ?? actionName,
- RouteUrlTranslationKey = routeAttribute.TranslationKey,
- HttpMethods = routeAttribute.HttpMethods,
- RouteName = routeName,
- IsAbsoluteUrl = routeAttribute.IsAbsoluteUrl,
- IgnoreRoutePrefix = routeAttribute.IgnoreRoutePrefix,
- IgnoreAreaUrl = routeAttribute.IgnoreAreaUrl,
- UseLowercaseRoute = routeAttribute.UseLowercaseRouteFlag,
- PreserveCaseForUrlParameters = routeAttribute.PreserveCaseForUrlParametersFlag,
- AppendTrailingSlash = routeAttribute.AppendTrailingSlashFlag
- }).ToList();
+ if (routePrefixAttributes.Any())
+ {
+ foreach (var routePrefixAttribute in routePrefixAttributes)
+ {
+ routeSpecs.Add(BuildRouteSpecification(controllerIndex, controllerType, actionMethod,
+ routeAreaAttribute, routePrefixAttribute,
+ routeAttribute));
+
+ // If ignoring the prefix, do not add the route again for other prefixes!
+ if (routeAttribute.IgnoreRoutePrefix)
+ break;
+ }
+ }
+ else
+ {
+ routeSpecs.Add(BuildRouteSpecification(controllerIndex, controllerType, actionMethod,
+ routeAreaAttribute, null, routeAttribute));
+ }
+ }
+ }
+ }
+
+ // Return specs ordered by route precedence:
+ return routeSpecs
+ .OrderBy(x => x.SitePrecedence)
+ .ThenBy(x => x.ControllerIndex)
+ .ThenBy(x => x.PrefixPrecedence)
+ .ThenBy(x => x.ControllerPrecedence)
+ .ThenBy(x => x.ActionPrecedence);
+ }
+
+ ///
+ /// Builds a from component parts.
+ ///
+ /// The index of this controller inthe registered controller types.
+ /// The controller type.
+ /// The action method info.
+ /// An applicable for the controller.
+ /// An applicable for the controller.
+ /// The for the action.
+ /// The route specification.
+ private RouteSpecification BuildRouteSpecification(int controllerIndex, Type controllerType, MethodInfo actionMethod, RouteAreaAttribute routeAreaAttribute, RoutePrefixAttribute routePrefixAttribute, IRouteAttribute routeAttribute)
+ {
+ var isAsyncController = controllerType.IsAsyncController();
+ var subdomain = GetAreaSubdomain(routeAreaAttribute);
+ var actionName = GetActionName(actionMethod, isAsyncController);
+
+ return new RouteSpecification
+ {
+ ControllerIndex = controllerIndex,
+ SitePrecedence = GetSortableOrder(routeAttribute.SitePrecedence),
+ ControllerPrecedence = GetSortableOrder(routeAttribute.ControllerPrecedence),
+ ActionPrecedence = GetSortableOrder(routeAttribute.ActionPrecedence),
+ AreaName = GetAreaName(routeAreaAttribute, controllerType),
+ AreaUrl = GetAreaUrl(routeAreaAttribute, subdomain, controllerType),
+ AreaUrlTranslationKey = routeAreaAttribute.SafeGet(a => a.TranslationKey),
+ Subdomain = subdomain,
+ ControllerType = controllerType,
+ ControllerName = controllerType.GetControllerName(),
+ ActionName = actionName,
+ RouteUrl = routeAttribute.RouteUrl ?? actionName,
+ RouteUrlTranslationKey = routeAttribute.TranslationKey,
+ HttpMethods = routeAttribute.HttpMethods,
+ RouteName = routeAttribute.RouteName,
+ IsAbsoluteUrl = routeAttribute.IsAbsoluteUrl,
+ IgnoreRoutePrefix = routeAttribute.IgnoreRoutePrefix,
+ IgnoreAreaUrl = routeAttribute.IgnoreAreaUrl,
+ UseLowercaseRoute = routeAttribute.UseLowercaseRouteFlag,
+ PreserveCaseForUrlParameters = routeAttribute.PreserveCaseForUrlParametersFlag,
+ AppendTrailingSlash = routeAttribute.AppendTrailingSlashFlag,
+ RoutePrefixUrl = GetRoutePrefixUrl(routePrefixAttribute, controllerType),
+ RoutePrefixUrlTranslationKey = routePrefixAttribute.SafeGet(a => a.TranslationKey),
+ PrefixPrecedence = GetSortableOrder(routePrefixAttribute.SafeGet(a => a.Precedence, int.MaxValue))
+ };
}
///
@@ -144,6 +181,18 @@ private static IEnumerable GetRouteAttributes(MethodInfo action
return attributes;
}
+ ///
+ /// Get a to use for the controller.
+ ///
+ /// The controller type.
+ /// An applicable for the controller.
+ /// An applicable .
+ private static RouteAreaAttribute GetRouteAreaAttribute(Type controllerType, RouteConventionAttributeBase convention)
+ {
+ return controllerType.GetCustomAttribute(true)
+ ?? convention.SafeGet(x => x.GetDefaultRouteArea(controllerType));
+ }
+
///
/// Gets the area name.
///
@@ -203,6 +252,35 @@ private string GetAreaSubdomain(RouteAreaAttribute routeAreaAttribute)
return subdomainOverride ?? routeAreaAttribute.Subdomain;
}
+ ///
+ /// Get a to use for the controller.
+ ///
+ /// The controller type.
+ /// An applicable for the controller.
+ /// The action method info.
+ /// A list of applicable .
+ private static IEnumerable GetRoutePrefixAttributes(Type controllerType, RouteConventionAttributeBase convention, MethodInfo actionMethod)
+ {
+ // If there are any explicit route prefixes defined, use them.
+ var routePrefixAttributes = controllerType.GetCustomAttributes(true).ToList();
+
+ // Otherwise apply conventional prefixes.
+ if (!routePrefixAttributes.Any() && convention != null)
+ {
+ var oldConventionalRoutePrefix = convention.GetDefaultRoutePrefix(actionMethod);
+ if (oldConventionalRoutePrefix.HasValue())
+ {
+ routePrefixAttributes.Add(new RoutePrefixAttribute(oldConventionalRoutePrefix));
+ }
+ else
+ {
+ routePrefixAttributes.AddRange(convention.GetDefaultRoutePrefixes(controllerType));
+ }
+ }
+
+ return routePrefixAttributes.OrderBy(a => GetSortableOrder(a.Precedence));
+ }
+
///
/// Gets the route prefix for the routes in the controller.
///
diff --git a/src/AttributeRouting/Framework/RouteSpecification.cs b/src/AttributeRouting/Framework/RouteSpecification.cs
index 923ff0e..597e064 100644
--- a/src/AttributeRouting/Framework/RouteSpecification.cs
+++ b/src/AttributeRouting/Framework/RouteSpecification.cs
@@ -44,5 +44,15 @@ public class RouteSpecification
public bool IgnoreRoutePrefix { get; set; }
public bool IgnoreAreaUrl { get; set; }
+
+ public int ControllerIndex { get; set; }
+
+ public long SitePrecedence { get; set; }
+
+ public long PrefixPrecedence { get; set; }
+
+ public long ControllerPrecedence { get; set; }
+
+ public long ActionPrecedence { get; set; }
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting/RouteConventionAttributeBase.cs b/src/AttributeRouting/RouteConventionAttributeBase.cs
index 219b4bf..cfb1134 100644
--- a/src/AttributeRouting/RouteConventionAttributeBase.cs
+++ b/src/AttributeRouting/RouteConventionAttributeBase.cs
@@ -34,9 +34,9 @@ public virtual string GetDefaultRoutePrefix(MethodInfo actionMethod)
///
/// The controller type.
/// A .
- public virtual RoutePrefixAttribute GetDefaultRoutePrefix(Type controllerType)
+ public virtual IEnumerable GetDefaultRoutePrefixes(Type controllerType)
{
- return null;
+ yield break;
}
///
diff --git a/src/AttributeRouting/RoutePrefixAttribute.cs b/src/AttributeRouting/RoutePrefixAttribute.cs
index 78bb182..0ab53d7 100644
--- a/src/AttributeRouting/RoutePrefixAttribute.cs
+++ b/src/AttributeRouting/RoutePrefixAttribute.cs
@@ -5,14 +5,17 @@ namespace AttributeRouting
///
/// Defines a prefix shared by all the routes defined in this controller.
///
- [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class RoutePrefixAttribute : Attribute
{
///
/// Defines a prefix shared by all the routes defined in this controller.
/// The url prefix will be the name of the controller without the "Controller" suffix.
///
- public RoutePrefixAttribute() { }
+ public RoutePrefixAttribute()
+ {
+ Precedence = int.MaxValue;
+ }
///
/// Defines a prefix shared by all the routes defined in this controller.
@@ -31,6 +34,16 @@ public RoutePrefixAttribute(string url)
///
public string Url { get; private set; }
+ ///
+ /// The order of the routes using the given prefix
+ /// among all the routes generated with prefixes for this controller.
+ ///
+ ///
+ /// Positive integers (including zero) denote top routes: 1 is first, 2 is second, etc....
+ /// Negative integers denote bottom routes: -1 is last, -2 is second to last, etc....
+ ///
+ public int Precedence { get; set; }
+
///
/// Key used by translation provider to lookup the translation for the .
///
From 69aae0498059fc049a894ce8cfe338a7c9afe2dd Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 11 Dec 2012 14:18:08 -0700
Subject: [PATCH 16/97] #156 - added action method to route data tokens.
---
.../Features/StandardUsage.feature | 2 +
.../Features/StandardUsage.feature.cs | 28 +++++----
.../Steps/StandardUsageSteps.cs | 9 +++
.../Subjects/RoutePrefixesControllers.cs | 2 +-
.../Framework/RouteBuilder.cs | 3 +-
.../Framework/RouteReflector.cs | 29 ++++-----
.../Framework/RouteSpecification.cs | 59 ++++++++++---------
.../Logging/AttributeRouteInfo.cs | 2 +-
8 files changed, 77 insertions(+), 57 deletions(-)
diff --git a/src/AttributeRouting.Specs/Features/StandardUsage.feature b/src/AttributeRouting.Specs/Features/StandardUsage.feature
index b986a10..8a73b75 100644
--- a/src/AttributeRouting.Specs/Features/StandardUsage.feature
+++ b/src/AttributeRouting.Specs/Features/StandardUsage.feature
@@ -8,6 +8,7 @@ Scenario Outline: Generating routes for an MVC controller
And the default for "controller" is "StandardUsage"
And the default for "action" is ""
And the namespace is "AttributeRouting.Specs.Subjects"
+ And the route has a data token for "actionMethod"
Examples:
| method | action | url |
@@ -34,6 +35,7 @@ Scenario Outline: Generating routes for an API controller
And the default for "controller" is "HttpStandardUsage"
And the default for "action" is ""
And the namespace is "AttributeRouting.Specs.Subjects.Http"
+ And the route has a data token for "actionMethod"
Examples:
| method | action | url |
diff --git a/src/AttributeRouting.Specs/Features/StandardUsage.feature.cs b/src/AttributeRouting.Specs/Features/StandardUsage.feature.cs
index 2721111..c604650 100644
--- a/src/AttributeRouting.Specs/Features/StandardUsage.feature.cs
+++ b/src/AttributeRouting.Specs/Features/StandardUsage.feature.cs
@@ -98,6 +98,8 @@ public virtual void GeneratingRoutesForAnMVCController(string method, string act
testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 10
testRunner.And("the namespace is \"AttributeRouting.Specs.Subjects\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 11
+ testRunner.And("the route has a data token for \"actionMethod\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -118,22 +120,24 @@ public virtual void GeneratingRoutesForAnMVCController(string method, string act
public virtual void GeneratingRoutesForAnAPIController(string method, string action, string url, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes for an API controller", exampleTags);
-#line 29
-this.ScenarioSetup(scenarioInfo);
#line 30
- testRunner.Given("I have registered the routes for the HttpStandardUsageController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+this.ScenarioSetup(scenarioInfo);
#line 31
- testRunner.When(string.Format("I fetch the routes for the HttpStandardUsageController\'s {0} action", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+ testRunner.Given("I have registered the routes for the HttpStandardUsageController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 32
- testRunner.Then(string.Format("the route is constrained to {0} requests", method), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+ testRunner.When(string.Format("I fetch the routes for the HttpStandardUsageController\'s {0} action", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 33
- testRunner.And(string.Format("the route url is \"{0}\"", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+ testRunner.Then(string.Format("the route is constrained to {0} requests", method), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 34
- testRunner.And("the default for \"controller\" is \"HttpStandardUsage\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+ testRunner.And(string.Format("the route url is \"{0}\"", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 35
- testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+ testRunner.And("the default for \"controller\" is \"HttpStandardUsage\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 36
+ testRunner.And(string.Format("the default for \"action\" is \"{0}\"", action), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 37
testRunner.And("the namespace is \"AttributeRouting.Specs.Subjects.Http\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 38
+ testRunner.And("the route has a data token for \"actionMethod\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -143,13 +147,13 @@ public virtual void GeneratingRoutesForAnAPIController(string method, string act
public virtual void RespondingToOPTIONSRequestsInAnAPIController()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Responding to OPTIONS requests in an API controller", ((string[])(null)));
-#line 52
+#line 54
this.ScenarioSetup(scenarioInfo);
-#line 53
+#line 55
testRunner.Given("I have registered the routes for the HttpStandardUsageController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 54
+#line 56
testRunner.When("an OPTIONS request for \"api\" is made", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 55
+#line 57
testRunner.Then("the Get action is matched", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
diff --git a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
index 8b4a54c..1b2e6f6 100644
--- a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
+++ b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
@@ -50,6 +50,15 @@ public void ThenTheNamespaceIs(string ns)
Assert.That(route.DataTokens["namespaces"], Is.EqualTo(new[] { ns }));
}
+ [Then(@"the route has a data token for ""(.*?)""")]
+ public void ThenTheRouteHasADataTokeFor(string key)
+ {
+ var route = ScenarioContext.Current.GetFetchedRoutes().FirstOrDefault();
+
+ Assert.That(route, Is.Not.Null);
+ Assert.That(route.DataTokens[key], Is.Not.Null);
+ }
+
[Then(@"the route is constrained to (.*?) requests")]
public void ThenTheRouteIsConstrainedToRequests(string method)
{
diff --git a/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs b/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs
index 92c673a..1bdf3c5 100644
--- a/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/RoutePrefixesControllers.cs
@@ -80,7 +80,7 @@ public ActionResult Index()
[RoutePrefix("SecondPrefix")]
public class MultipleRoutePrefixController : Controller
{
- [GET("Index")]
+ [GET("Index", ActionPrecedence = 1)]
[GET("This/Is/Absolute", IsAbsoluteUrl = true)]
public ActionResult Index()
{
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 35fd328..7910e2e 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -346,7 +346,8 @@ private IDictionary CreateRouteDataTokens(RouteSpecification rou
{
var dataTokens = new Dictionary
{
- { "namespaces", new[] { routeSpec.ControllerType.Namespace } }
+ { "namespaces", new[] { routeSpec.ControllerType.Namespace } },
+ { "actionMethod", routeSpec.ActionMethod }
};
if (routeSpec.AreaName.HasValue())
diff --git a/src/AttributeRouting/Framework/RouteReflector.cs b/src/AttributeRouting/Framework/RouteReflector.cs
index ae3ddd3..b7168ea 100644
--- a/src/AttributeRouting/Framework/RouteReflector.cs
+++ b/src/AttributeRouting/Framework/RouteReflector.cs
@@ -118,30 +118,31 @@ private RouteSpecification BuildRouteSpecification(int controllerIndex, Type con
return new RouteSpecification
{
- ControllerIndex = controllerIndex,
- SitePrecedence = GetSortableOrder(routeAttribute.SitePrecedence),
- ControllerPrecedence = GetSortableOrder(routeAttribute.ControllerPrecedence),
+ ActionMethod = actionMethod,
+ ActionName = actionName,
ActionPrecedence = GetSortableOrder(routeAttribute.ActionPrecedence),
+ AppendTrailingSlash = routeAttribute.AppendTrailingSlashFlag,
AreaName = GetAreaName(routeAreaAttribute, controllerType),
AreaUrl = GetAreaUrl(routeAreaAttribute, subdomain, controllerType),
AreaUrlTranslationKey = routeAreaAttribute.SafeGet(a => a.TranslationKey),
- Subdomain = subdomain,
- ControllerType = controllerType,
+ ControllerIndex = controllerIndex,
ControllerName = controllerType.GetControllerName(),
- ActionName = actionName,
- RouteUrl = routeAttribute.RouteUrl ?? actionName,
- RouteUrlTranslationKey = routeAttribute.TranslationKey,
+ ControllerPrecedence = GetSortableOrder(routeAttribute.ControllerPrecedence),
+ ControllerType = controllerType,
HttpMethods = routeAttribute.HttpMethods,
- RouteName = routeAttribute.RouteName,
- IsAbsoluteUrl = routeAttribute.IsAbsoluteUrl,
- IgnoreRoutePrefix = routeAttribute.IgnoreRoutePrefix,
IgnoreAreaUrl = routeAttribute.IgnoreAreaUrl,
- UseLowercaseRoute = routeAttribute.UseLowercaseRouteFlag,
+ IgnoreRoutePrefix = routeAttribute.IgnoreRoutePrefix,
+ IsAbsoluteUrl = routeAttribute.IsAbsoluteUrl,
+ PrefixPrecedence = GetSortableOrder(routePrefixAttribute.SafeGet(a => a.Precedence, int.MaxValue)),
PreserveCaseForUrlParameters = routeAttribute.PreserveCaseForUrlParametersFlag,
- AppendTrailingSlash = routeAttribute.AppendTrailingSlashFlag,
+ RouteName = routeAttribute.RouteName,
RoutePrefixUrl = GetRoutePrefixUrl(routePrefixAttribute, controllerType),
RoutePrefixUrlTranslationKey = routePrefixAttribute.SafeGet(a => a.TranslationKey),
- PrefixPrecedence = GetSortableOrder(routePrefixAttribute.SafeGet(a => a.Precedence, int.MaxValue))
+ RouteUrl = routeAttribute.RouteUrl ?? actionName,
+ RouteUrlTranslationKey = routeAttribute.TranslationKey,
+ SitePrecedence = GetSortableOrder(routeAttribute.SitePrecedence),
+ Subdomain = subdomain,
+ UseLowercaseRoute = routeAttribute.UseLowercaseRouteFlag,
};
}
diff --git a/src/AttributeRouting/Framework/RouteSpecification.cs b/src/AttributeRouting/Framework/RouteSpecification.cs
index 597e064..ed454c6 100644
--- a/src/AttributeRouting/Framework/RouteSpecification.cs
+++ b/src/AttributeRouting/Framework/RouteSpecification.cs
@@ -1,4 +1,5 @@
using System;
+using System.Reflection;
namespace AttributeRouting.Framework
{
@@ -7,52 +8,54 @@ namespace AttributeRouting.Framework
///
public class RouteSpecification
{
+ public MethodInfo ActionMethod { get; set; }
+
+ public string ActionName { get; set; }
+
+ public long ActionPrecedence { get; set; }
+
+ public bool? AppendTrailingSlash { get; set; }
+
public string AreaName { get; set; }
public string AreaUrl { get; set; }
public string AreaUrlTranslationKey { get; set; }
+
+ public int ControllerIndex { get; set; }
+
+ public string ControllerName { get; set; }
+
+ public long ControllerPrecedence { get; set; }
+
+ public Type ControllerType { get; set; }
+ public string[] HttpMethods { get; set; }
+
+ public bool IgnoreAreaUrl { get; set; }
+
+ public bool IgnoreRoutePrefix { get; set; }
+
+ public bool IsAbsoluteUrl { get; set; }
+
+ public long PrefixPrecedence { get; set; }
+
+ public bool? PreserveCaseForUrlParameters { get; set; }
+
public string RoutePrefixUrl { get; set; }
public string RoutePrefixUrlTranslationKey { get; set; }
- public Type ControllerType { get; set; }
-
- public string ControllerName { get; set; }
-
- public string ActionName { get; set; }
-
public string RouteUrl { get; set; }
public string RouteUrlTranslationKey { get; set; }
- public string[] HttpMethods { get; set; }
-
public string RouteName { get; set; }
- public bool IsAbsoluteUrl { get; set; }
-
+ public long SitePrecedence { get; set; }
+
public string Subdomain { get; set; }
public bool? UseLowercaseRoute { get; set; }
-
- public bool? PreserveCaseForUrlParameters { get; set; }
-
- public bool? AppendTrailingSlash { get; set; }
-
- public bool IgnoreRoutePrefix { get; set; }
-
- public bool IgnoreAreaUrl { get; set; }
-
- public int ControllerIndex { get; set; }
-
- public long SitePrecedence { get; set; }
-
- public long PrefixPrecedence { get; set; }
-
- public long ControllerPrecedence { get; set; }
-
- public long ActionPrecedence { get; set; }
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting/Logging/AttributeRouteInfo.cs b/src/AttributeRouting/Logging/AttributeRouteInfo.cs
index 2a1e0b4..c565d1c 100644
--- a/src/AttributeRouting/Logging/AttributeRouteInfo.cs
+++ b/src/AttributeRouting/Logging/AttributeRouteInfo.cs
@@ -115,7 +115,7 @@ public static AttributeRouteInfo GetRouteInfo(string url,
{
item.DataTokens.Add(token.Key, ((string[]) token.Value).Aggregate((n1, n2) => n1 + ", " + n2));
}
- else
+ else if (!token.Key.ValueEquals("actionMethod"))
{
item.DataTokens.Add(token.Key, token.Value.ToString());
}
From 461172b002d5935923256a6c5b5e1592cb0b6325 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 11 Dec 2012 15:18:39 -0700
Subject: [PATCH 17/97] #146 - AR's custom http method constraint was
interfering with IHostBufferPolicySelector, because it was checking for the
incoming http method too early in the pipeline.
---
.../Steps/StandardUsageSteps.cs | 16 +++++----
.../Areas/Api/Controllers/UploadController.cs | 18 ++++++++++
.../AttributeRouting.Tests.Web.csproj | 2 ++
.../CustomWebHostBufferPolicySelector.cs | 23 ++++++++++++
src/AttributeRouting.Tests.Web/Global.asax.cs | 4 +++
.../AttributeRouting.Web.Http.SelfHost.csproj | 1 -
.../RestfulHttpMethodConstraint.cs | 23 ------------
.../Factories/RouteConstraintFactory.cs | 7 ----
.../Factories/RouteConstraintFactory.cs | 5 ---
.../Factories/RouteConstraintFactory.cs | 5 ---
.../RouteAttribute.cs | 3 ++
.../AttributeRouting.Web.csproj | 1 -
.../RestfulHttpMethodConstraint.cs | 35 -------------------
.../Helpers/HttpRequestBaseExtensions.cs | 9 -----
src/AttributeRouting/AttributeRouting.csproj | 1 -
.../IRestfulHttpMethodConstraint.cs | 22 ------------
.../Factories/IRouteConstraintFactory.cs | 5 ---
.../Framework/RouteBuilder.cs | 20 +++++------
.../Logging/AttributeRouteInfo.cs | 10 +++---
19 files changed, 74 insertions(+), 136 deletions(-)
create mode 100644 src/AttributeRouting.Tests.Web/Areas/Api/Controllers/UploadController.cs
create mode 100644 src/AttributeRouting.Tests.Web/CustomWebHostBufferPolicySelector.cs
delete mode 100644 src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs
delete mode 100644 src/AttributeRouting.Web/Constraints/RestfulHttpMethodConstraint.cs
delete mode 100644 src/AttributeRouting/Constraints/IRestfulHttpMethodConstraint.cs
diff --git a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
index 1b2e6f6..b61bb84 100644
--- a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
+++ b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
@@ -1,5 +1,7 @@
using System;
+using System.Collections.Generic;
using System.Linq;
+using AttributeRouting.Framework;
using AttributeRouting.Web.Constraints;
using NUnit.Framework;
using TechTalk.SpecFlow;
@@ -66,16 +68,16 @@ public void ThenTheRouteIsConstrainedToRequests(string method)
Assert.That(route, Is.Not.Null);
- var constraint = route.Constraints["httpMethod"] as RestfulHttpMethodConstraint;
+ var allowedMethods = route.DataTokens["httpMethods"] as IEnumerable;
if (method.HasValue())
{
- Assert.That(constraint, Is.Not.Null);
- Assert.That(constraint.AllowedMethods.Any(m => m.Equals(method, StringComparison.OrdinalIgnoreCase)), Is.True);
+ Assert.That(allowedMethods, Is.Not.Null);
+ Assert.That(allowedMethods.Any(m => m.Equals(method, StringComparison.OrdinalIgnoreCase)), Is.True);
}
else
{
- Assert.That(constraint, Is.Null);
+ Assert.That(allowedMethods, Is.Null);
}
}
@@ -86,10 +88,10 @@ public void ThenTheRouteForIsConstrainedToRequests(string action, string method)
Assert.That(route, Is.Not.Null);
- var constraint = route.Constraints["httpMethod"] as RestfulHttpMethodConstraint;
+ var allowedMethods = route.DataTokens["httpMethods"] as IEnumerable;
- Assert.That(constraint, Is.Not.Null);
- Assert.That(constraint.AllowedMethods.Any(m => m.Equals(method, StringComparison.OrdinalIgnoreCase)), Is.True);
+ Assert.That(allowedMethods, Is.Not.Null);
+ Assert.That(allowedMethods.Any(m => m.Equals(method, StringComparison.OrdinalIgnoreCase)), Is.True);
}
}
}
diff --git a/src/AttributeRouting.Tests.Web/Areas/Api/Controllers/UploadController.cs b/src/AttributeRouting.Tests.Web/Areas/Api/Controllers/UploadController.cs
new file mode 100644
index 0000000..d3e6253
--- /dev/null
+++ b/src/AttributeRouting.Tests.Web/Areas/Api/Controllers/UploadController.cs
@@ -0,0 +1,18 @@
+using AttributeRouting.Web.Http;
+
+namespace AttributeRouting.Tests.Web.Areas.Api.Controllers
+{
+ ///
+ /// To test issue 146
+ ///
+ [RoutePrefix("upload")]
+ public class UploadController : BaseApiController
+ {
+ // POST /api/plain
+ [POST("")]
+ public string Post()
+ {
+ return "foo";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj b/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj
index 277dbbf..b36614c 100644
--- a/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj
+++ b/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj
@@ -118,6 +118,7 @@
+
@@ -135,6 +136,7 @@
+
Global.asax
diff --git a/src/AttributeRouting.Tests.Web/CustomWebHostBufferPolicySelector.cs b/src/AttributeRouting.Tests.Web/CustomWebHostBufferPolicySelector.cs
new file mode 100644
index 0000000..9d15c22
--- /dev/null
+++ b/src/AttributeRouting.Tests.Web/CustomWebHostBufferPolicySelector.cs
@@ -0,0 +1,23 @@
+using System.Web.Http.WebHost;
+
+namespace AttributeRouting.Tests.Web
+{
+ public class CustomWebHostBufferPolicySelector : WebHostBufferPolicySelector
+ {
+ public override bool UseBufferedInputStream(object hostContext)
+ {
+ var contextBase = hostContext as System.Web.HttpContextBase;
+
+ if (contextBase != null
+ && contextBase.Request.ContentType != null
+ && contextBase.Request.ContentType.Contains("multipart"))
+ {
+ // we are enabling streamed mode here
+ return false;
+ }
+
+ // let the default behavior(buffered mode) to handle the scenario
+ return base.UseBufferedInputStream(hostContext);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests.Web/Global.asax.cs b/src/AttributeRouting.Tests.Web/Global.asax.cs
index 763b31a..7ca3a41 100644
--- a/src/AttributeRouting.Tests.Web/Global.asax.cs
+++ b/src/AttributeRouting.Tests.Web/Global.asax.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Web.Http;
+using System.Web.Http.Hosting;
using System.Web.Mvc;
using System.Web.Routing;
using AttributeRouting.Framework.Localization;
@@ -88,6 +89,9 @@ public static void RegisterRoutes(RouteCollection routes)
"{*path}",
new { controller = "home", action = "filenotfound" },
new[] { typeof(HomeController).Namespace });
+
+ // Testing issue 146
+ GlobalConfiguration.Configuration.Services.Replace(typeof(IHostBufferPolicySelector), new CustomWebHostBufferPolicySelector());
}
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
index 9e55fab..ea1fdee 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
+++ b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
@@ -72,7 +72,6 @@
-
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs b/src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs
deleted file mode 100644
index c103c4c..0000000
--- a/src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Net.Http;
-using System.Web.Http.Routing;
-using AttributeRouting.Constraints;
-
-namespace AttributeRouting.Web.Http.SelfHost.Constraints
-{
- ///
- /// Constrains a route by the specified allowed HTTP methods.
- ///
- public class RestfulHttpMethodConstraint : HttpMethodConstraint, IRestfulHttpMethodConstraint
- {
- public RestfulHttpMethodConstraint(params HttpMethod[] allowedMethods)
- : base(allowedMethods) {}
-
- ICollection IRestfulHttpMethodConstraint.AllowedMethods
- {
- get { return new ReadOnlyCollection(AllowedMethods.Select(method => method.Method).ToList()); }
- }
- }
-}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
index 2133c36..7523417 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
@@ -1,6 +1,5 @@
using System;
using System.Linq;
-using System.Net.Http;
using System.Text.RegularExpressions;
using System.Web.Http.Routing;
using AttributeRouting.Constraints;
@@ -26,12 +25,6 @@ public RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, Regex
return new RegexRouteConstraint(pattern, options);
}
- public IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods)
- {
- var allowedMethods = httpMethods.Select(m => new HttpMethod(m)).ToArray();
- return new RestfulHttpMethodConstraint(allowedMethods);
- }
-
public object CreateInlineRouteConstraint(string name, params object[] parameters)
{
var inlineRouteConstraints = _configuration.InlineRouteConstraints;
diff --git a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
index 50f5f98..9681fa9 100644
--- a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
@@ -25,11 +25,6 @@ public RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, Regex
return new RegexRouteConstraint(pattern, options);
}
- public IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods)
- {
- return new RestfulHttpMethodConstraint(httpMethods);
- }
-
public object CreateInlineRouteConstraint(string name, params object[] parameters)
{
var inlineRouteConstraints = _configuration.InlineRouteConstraints;
diff --git a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
index 72b09f1..3d09d9d 100644
--- a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
@@ -25,11 +25,6 @@ public RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, Regex
return new RegexRouteConstraint(pattern, options);
}
- public IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods)
- {
- return new RestfulHttpMethodConstraint(httpMethods);
- }
-
public object CreateInlineRouteConstraint(string name, params object[] parameters)
{
var inlineRouteConstraints = _configuration.InlineRouteConstraints;
diff --git a/src/AttributeRouting.Web.Mvc/RouteAttribute.cs b/src/AttributeRouting.Web.Mvc/RouteAttribute.cs
index a5b0523..edcece5 100644
--- a/src/AttributeRouting.Web.Mvc/RouteAttribute.cs
+++ b/src/AttributeRouting.Web.Mvc/RouteAttribute.cs
@@ -122,10 +122,13 @@ public bool AppendTrailingSlash
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
+ if (controllerContext == null) throw new ArgumentNullException("controllerContext");
+
if (!HttpMethods.Any())
return true;
var method = controllerContext.HttpContext.Request.GetHttpMethodOverride();
+
return HttpMethods.Any(m => m.ValueEquals(method));
}
}
diff --git a/src/AttributeRouting.Web/AttributeRouting.Web.csproj b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
index ebc0c46..cc59709 100644
--- a/src/AttributeRouting.Web/AttributeRouting.Web.csproj
+++ b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
@@ -65,7 +65,6 @@
-
diff --git a/src/AttributeRouting.Web/Constraints/RestfulHttpMethodConstraint.cs b/src/AttributeRouting.Web/Constraints/RestfulHttpMethodConstraint.cs
deleted file mode 100644
index c5bdb1a..0000000
--- a/src/AttributeRouting.Web/Constraints/RestfulHttpMethodConstraint.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System.Linq;
-using System.Web;
-using System.Web.Routing;
-using AttributeRouting.Constraints;
-using AttributeRouting.Helpers;
-using AttributeRouting.Web.Helpers;
-
-namespace AttributeRouting.Web.Constraints
-{
- ///
- /// Constrains a route by the specified allowed HTTP methods.
- ///
- ///
- /// AttributeRouting uses its own implementation of the Match method
- /// to handle X-HTTP-Method-Override values in the request headers, form, and query collections.
- ///
- public class RestfulHttpMethodConstraint : HttpMethodConstraint, IRestfulHttpMethodConstraint
- {
- ///
- /// Constrain a route by the specified allowed HTTP methods.
- ///
- public RestfulHttpMethodConstraint(params string[] allowedMethods)
- : base(allowedMethods) { }
-
- protected override bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
- {
- if (routeDirection == RouteDirection.UrlGeneration)
- return true;
-
- var httpMethod = httpContext.Request.GetHttpMethod();
-
- return AllowedMethods.Any(m => m.ValueEquals(httpMethod));
- }
- }
-}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs b/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs
index 6e0f599..5cad7ee 100644
--- a/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs
+++ b/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs
@@ -1,7 +1,6 @@
using System.Collections.Specialized;
using System.Reflection;
using System.Web;
-using AttributeRouting.Helpers;
namespace AttributeRouting.Web.Helpers
{
@@ -46,13 +45,5 @@ private static string GetUnvalidatedCollectionValue(this HttpRequestBase request
return null;
}
}
-
- public static string GetHttpMethod(this HttpRequestBase request)
- {
- return request.SafeGet(r => r.Headers["X-HTTP-Method-Override"]) ??
- request.SafeGet(r => GetFormValue(r, "X-HTTP-Method-Override")) ??
- request.SafeGet(r => GetQueryStringValue(r, "X-HTTP-Method-Override")) ??
- request.SafeGet(r => r.HttpMethod, "GET");
- }
}
}
diff --git a/src/AttributeRouting/AttributeRouting.csproj b/src/AttributeRouting/AttributeRouting.csproj
index f7d136e..0eda025 100644
--- a/src/AttributeRouting/AttributeRouting.csproj
+++ b/src/AttributeRouting/AttributeRouting.csproj
@@ -64,7 +64,6 @@
-
diff --git a/src/AttributeRouting/Constraints/IRestfulHttpMethodConstraint.cs b/src/AttributeRouting/Constraints/IRestfulHttpMethodConstraint.cs
deleted file mode 100644
index 70ab210..0000000
--- a/src/AttributeRouting/Constraints/IRestfulHttpMethodConstraint.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System.Collections.Generic;
-using AttributeRouting.Framework.Factories;
-
-namespace AttributeRouting.Constraints
-{
- ///
- /// Abstraction used by
- /// when applying restful route constraints.
- ///
- ///
- /// Due to
- /// System.Web.Routing.HttpMethodConstraint (used in web-hosted scenarios) and
- /// System.Web.Http.Routing.HttpMethodConstraint (used in self-hosted scenarios).
- ///
- public interface IRestfulHttpMethodConstraint
- {
- ///
- /// The allowed HTTP methods.
- ///
- ICollection AllowedMethods { get; }
- }
-}
\ No newline at end of file
diff --git a/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs b/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs
index 708c0c4..db34075 100644
--- a/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs
+++ b/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs
@@ -19,11 +19,6 @@ public interface IRouteConstraintFactory
///
RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, RegexOptions options = RegexOptions.None);
- ///
- /// Creates a new RestfulHttpMethodConstraint
- ///
- IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods);
-
///
/// Creates an inline constraint of a specific type with the given parameters.
///
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 7910e2e..41b54a4 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -196,10 +196,6 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
{
var constraints = new Dictionary();
- // Default constraints
- if (routeSpec.HttpMethods.Any())
- constraints.Add("httpMethod", _routeConstraintFactory.CreateRestfulHttpMethodConstraint(routeSpec.HttpMethods));
-
// Work from a complete, tokenized url; ie: support constraints in area urls, route prefix urls, and route urls.
var tokenizedUrl = BuildTokenizedUrl(routeSpec.RouteUrl, routeSpec.RoutePrefixUrl, routeSpec.AreaUrl, routeSpec);
var urlParameters = GetUrlParameterContents(tokenizedUrl).ToList();
@@ -212,7 +208,6 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
var queryStringParameters = urlParameters.Except(pathOnlyUrlParameters).ToList();
// Inline constraints
- var constraintFactory = _configuration.RouteConstraintFactory;
foreach (var parameter in urlParameters)
{
// Keep track of whether this param is optional or in the querystring,
@@ -255,13 +250,13 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
? new[] {constraintParamsRaw}
: constraintParamsRaw.SplitAndTrim(",");
- constraint = constraintFactory.CreateInlineRouteConstraint(constraintName, constraintParams);
+ constraint = _routeConstraintFactory.CreateInlineRouteConstraint(constraintName, constraintParams);
}
else
{
// Constraint of the form "id:int"
constraintName = definition;
- constraint = constraintFactory.CreateInlineRouteConstraint(constraintName);
+ constraint = _routeConstraintFactory.CreateInlineRouteConstraint(constraintName);
}
if (constraint == null)
@@ -277,7 +272,7 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
// 1. If more than one constraint, wrap in a compound constraint.
if (inlineConstraints.Count > 1)
{
- finalConstraint = constraintFactory.CreateCompoundRouteConstraint(inlineConstraints.ToArray());
+ finalConstraint = _routeConstraintFactory.CreateCompoundRouteConstraint(inlineConstraints.ToArray());
}
else
{
@@ -287,13 +282,13 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
// 2. If the constraint is in the querystring, wrap in a query string constraint.
if (parameterIsInQueryString)
{
- finalConstraint = constraintFactory.CreateQueryStringRouteConstraint(finalConstraint);
+ finalConstraint = _routeConstraintFactory.CreateQueryStringRouteConstraint(finalConstraint);
}
// 3. If the constraint is optional, wrap in an optional constraint.
if (parameterIsOptional)
{
- finalConstraint = constraintFactory.CreateOptionalRouteConstraint(finalConstraint);
+ finalConstraint = _routeConstraintFactory.CreateOptionalRouteConstraint(finalConstraint);
}
constraints.Add(parameterName, finalConstraint);
@@ -350,6 +345,11 @@ private IDictionary CreateRouteDataTokens(RouteSpecification rou
{ "actionMethod", routeSpec.ActionMethod }
};
+ if (routeSpec.HttpMethods.Any())
+ {
+ dataTokens.Add("httpMethods", routeSpec.HttpMethods);
+ }
+
if (routeSpec.AreaName.HasValue())
{
dataTokens.Add("area", routeSpec.AreaName);
diff --git a/src/AttributeRouting/Logging/AttributeRouteInfo.cs b/src/AttributeRouting/Logging/AttributeRouteInfo.cs
index c565d1c..08373fe 100644
--- a/src/AttributeRouting/Logging/AttributeRouteInfo.cs
+++ b/src/AttributeRouting/Logging/AttributeRouteInfo.cs
@@ -51,11 +51,7 @@ public static AttributeRouteInfo GetRouteInfo(string url,
if (constraint.Value == null)
continue;
- if (constraint.Value is IRestfulHttpMethodConstraint)
- {
- item.HttpMethods = String.Join(", ", ((IRestfulHttpMethodConstraint)constraint.Value).AllowedMethods);
- }
- else if (constraint.Value is RegexRouteConstraintBase)
+ if (constraint.Value is RegexRouteConstraintBase)
{
item.Constraints.Add(constraint.Key, ((RegexRouteConstraintBase)constraint.Value).Pattern);
}
@@ -115,6 +111,10 @@ public static AttributeRouteInfo GetRouteInfo(string url,
{
item.DataTokens.Add(token.Key, ((string[]) token.Value).Aggregate((n1, n2) => n1 + ", " + n2));
}
+ else if (token.Key.ValueEquals("httpMethods"))
+ {
+ item.HttpMethods = ((string[]) token.Value).Aggregate((n1, n2) => n1 + ", " + n2);
+ }
else if (!token.Key.ValueEquals("actionMethod"))
{
item.DataTokens.Add(token.Key, token.Value.ToString());
From 89ac50486e9493877e44c077315276a01eed73d4 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 11 Dec 2012 15:55:18 -0700
Subject: [PATCH 18/97] #124 - supporting IRouteHandler in web-host scenario by
plugging into the RouteTable.MapHttpRoute method if the route handler factory
is supplied.
---
.../HttpRouteCollectionExtensions.cs | 21 +++++++++++++++++--
.../HttpWebAttributeRoutingConfiguration.cs | 3 +--
.../Framework/HttpAttributeRoute.cs | 2 +-
.../HttpAttributeRoutingConfigurationBase.cs | 5 +++++
4 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs b/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
index 65f5eb2..3cf1308 100644
--- a/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
@@ -2,6 +2,7 @@
using System.Linq;
using System.Reflection;
using System.Web.Http;
+using System.Web.Routing;
using AttributeRouting.Framework;
using AttributeRouting.Web.Http.Framework;
@@ -51,9 +52,25 @@ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, HttpW
private static void MapHttpAttributeRoutesInternal(this HttpRouteCollection routes, HttpWebAttributeRoutingConfiguration configuration)
{
- var generatedRoutes = new RouteBuilder(configuration).BuildAllRoutes();
+ var generatedRoutes = new RouteBuilder(configuration).BuildAllRoutes().Cast().ToList();
- generatedRoutes.ToList().ForEach(r => routes.Add(r.RouteName, (HttpAttributeRoute)r));
+ // If providing a custom IRouteHandler via config, add the routes to the RouteCollection.
+ // Have to do this because the HttpRoutes do not support the functionality.
+ var routeHandler = configuration.RouteHandlerFactory();
+ if (routeHandler != null)
+ {
+ var mvcRoutes = RouteTable.Routes;
+ generatedRoutes.ForEach(r =>
+ {
+ var mvcRoute = mvcRoutes.MapHttpRoute(r.RouteName, r.Url, r.Defaults, r.Constraints, r.Handler);
+ mvcRoute.RouteHandler = routeHandler;
+ });
+ }
+ else
+ {
+ // Otherwise, add them to the HttpRouteCollection.
+ generatedRoutes.ForEach(r => routes.Add(r.RouteName, r));
+ }
}
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http.WebHost/HttpWebAttributeRoutingConfiguration.cs b/src/AttributeRouting.Web.Http.WebHost/HttpWebAttributeRoutingConfiguration.cs
index ef836f4..b9e767c 100644
--- a/src/AttributeRouting.Web.Http.WebHost/HttpWebAttributeRoutingConfiguration.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/HttpWebAttributeRoutingConfiguration.cs
@@ -1,5 +1,4 @@
using System;
-using System.Web.Http.WebHost;
using System.Web.Routing;
using AttributeRouting.Web.Http.WebHost.Framework.Factories;
@@ -13,7 +12,7 @@ public HttpWebAttributeRoutingConfiguration()
ParameterFactory = new RouteParameterFactory();
RouteConstraintFactory = new RouteConstraintFactory(this);
- RouteHandlerFactory = () => HttpControllerRouteHandler.Instance;
+ RouteHandlerFactory = () => null;
RegisterDefaultInlineRouteConstraints(typeof(Web.Constraints.RegexRouteConstraint).Assembly);
}
diff --git a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
index 994dc8f..51e5f61 100644
--- a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
+++ b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
@@ -23,7 +23,7 @@ public HttpAttributeRoute(string url,
HttpRouteValueDictionary constraints,
HttpRouteValueDictionary dataTokens,
HttpAttributeRoutingConfigurationBase configuration)
- : base(url, defaults, constraints, dataTokens)
+ : base(url, defaults, constraints, dataTokens, configuration.MessageHandler)
{
_configuration = configuration;
}
diff --git a/src/AttributeRouting.Web.Http/HttpAttributeRoutingConfigurationBase.cs b/src/AttributeRouting.Web.Http/HttpAttributeRoutingConfigurationBase.cs
index 85604a7..c1f06bc 100644
--- a/src/AttributeRouting.Web.Http/HttpAttributeRoutingConfigurationBase.cs
+++ b/src/AttributeRouting.Web.Http/HttpAttributeRoutingConfigurationBase.cs
@@ -13,6 +13,11 @@ protected HttpAttributeRoutingConfigurationBase()
CurrentUICultureResolver = (ctx, data) => Thread.CurrentThread.CurrentUICulture.Name;
}
+ ///
+ /// The message handler that will be the recipient of the request.
+ ///
+ public HttpMessageHandler MessageHandler { get; set; }
+
///
/// The controller type applicable to this context.
///
From 1aa492d43e57e5d218e2809ac1ebb0809d08994b Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 11 Dec 2012 19:23:10 -0700
Subject: [PATCH 19/97] fixed a couple of bugs introduced in new stuff for v3.4
---
.../Steps/StandardUsageSteps.cs | 34 +++++++++----------
.../AttributeRouting.Web.Http.SelfHost.csproj | 1 +
.../RestfulHttpMethodConstraint.cs | 25 ++++++++++++++
.../Factories/RouteConstraintFactory.cs | 7 ++++
.../Factories/RouteConstraintFactory.cs | 5 +++
.../HttpRouteCollectionExtensions.cs | 1 +
.../Factories/RouteConstraintFactory.cs | 5 +++
.../AttributeRouting.Web.csproj | 1 +
.../RestfulHttpMethodConstraint.cs | 32 +++++++++++++++++
.../Helpers/HttpRequestBaseExtensions.cs | 9 +++++
src/AttributeRouting/AttributeRouting.csproj | 1 +
.../IRestfulHttpMethodConstraint.cs | 22 ++++++++++++
.../Framework/AttributeRouteExtensions.cs | 5 +--
.../Factories/IRouteConstraintFactory.cs | 5 +++
.../Framework/RouteBuilder.cs | 4 +++
.../Logging/AttributeRouteInfo.cs | 6 ++--
16 files changed, 141 insertions(+), 22 deletions(-)
create mode 100644 src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs
create mode 100644 src/AttributeRouting.Web/Constraints/RestfulHttpMethodConstraint.cs
create mode 100644 src/AttributeRouting/Constraints/IRestfulHttpMethodConstraint.cs
diff --git a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
index b61bb84..8f0f9b3 100644
--- a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
+++ b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Web.Routing;
+using AttributeRouting.Constraints;
using AttributeRouting.Framework;
using AttributeRouting.Web.Constraints;
using NUnit.Framework;
@@ -66,19 +68,7 @@ public void ThenTheRouteIsConstrainedToRequests(string method)
{
var route = ScenarioContext.Current.GetFetchedRoutes().FirstOrDefault();
- Assert.That(route, Is.Not.Null);
-
- var allowedMethods = route.DataTokens["httpMethods"] as IEnumerable;
-
- if (method.HasValue())
- {
- Assert.That(allowedMethods, Is.Not.Null);
- Assert.That(allowedMethods.Any(m => m.Equals(method, StringComparison.OrdinalIgnoreCase)), Is.True);
- }
- else
- {
- Assert.That(allowedMethods, Is.Null);
- }
+ AssertThatRouteIsConstrainedToHttpMethod(route, method);
}
[Then(@"the route for (.*?) is constrained to (.*?) requests")]
@@ -86,12 +76,22 @@ public void ThenTheRouteForIsConstrainedToRequests(string action, string method)
{
var route = ScenarioContext.Current.GetFetchedRoutes().FirstOrDefault(r => r.Defaults["action"].ToString() == action);
- Assert.That(route, Is.Not.Null);
+ AssertThatRouteIsConstrainedToHttpMethod(route, method);
+ }
- var allowedMethods = route.DataTokens["httpMethods"] as IEnumerable;
+ private void AssertThatRouteIsConstrainedToHttpMethod(Route route, string method)
+ {
+ var constraint = route.Constraints["httpMethod"] as IRestfulHttpMethodConstraint;
- Assert.That(allowedMethods, Is.Not.Null);
- Assert.That(allowedMethods.Any(m => m.Equals(method, StringComparison.OrdinalIgnoreCase)), Is.True);
+ if (method.HasValue())
+ {
+ Assert.That(constraint, Is.Not.Null);
+ Assert.That(constraint.AllowedMethods.Any(m => m.Equals(method, StringComparison.OrdinalIgnoreCase)), Is.True);
+ }
+ else
+ {
+ Assert.That(constraint, Is.Null);
+ }
}
}
}
diff --git a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
index ea1fdee..4c584e0 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
+++ b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
@@ -68,6 +68,7 @@
SharedAssemblyInfo.cs
+
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs b/src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs
new file mode 100644
index 0000000..6d94f39
--- /dev/null
+++ b/src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Net.Http;
+using System.Web.Http.Routing;
+using AttributeRouting.Constraints;
+
+namespace AttributeRouting.Web.Http.SelfHost.Constraints
+{
+ ///
+ /// Constrains a route by HTTP method.
+ ///
+ public class RestfulHttpMethodConstraint : HttpMethodConstraint, IRestfulHttpMethodConstraint
+ {
+ public RestfulHttpMethodConstraint(params HttpMethod[] allowedMethods)
+ : base(allowedMethods)
+ {
+ }
+
+ ICollection IRestfulHttpMethodConstraint.AllowedMethods
+ {
+ get { return new ReadOnlyCollection(AllowedMethods.Select(method => method.Method).ToList()); }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
index 7523417..2133c36 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq;
+using System.Net.Http;
using System.Text.RegularExpressions;
using System.Web.Http.Routing;
using AttributeRouting.Constraints;
@@ -25,6 +26,12 @@ public RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, Regex
return new RegexRouteConstraint(pattern, options);
}
+ public IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods)
+ {
+ var allowedMethods = httpMethods.Select(m => new HttpMethod(m)).ToArray();
+ return new RestfulHttpMethodConstraint(allowedMethods);
+ }
+
public object CreateInlineRouteConstraint(string name, params object[] parameters)
{
var inlineRouteConstraints = _configuration.InlineRouteConstraints;
diff --git a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
index 9681fa9..50f5f98 100644
--- a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
@@ -25,6 +25,11 @@ public RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, Regex
return new RegexRouteConstraint(pattern, options);
}
+ public IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods)
+ {
+ return new RestfulHttpMethodConstraint(httpMethods);
+ }
+
public object CreateInlineRouteConstraint(string name, params object[] parameters)
{
var inlineRouteConstraints = _configuration.InlineRouteConstraints;
diff --git a/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs b/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
index 3cf1308..b219eb4 100644
--- a/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
@@ -63,6 +63,7 @@ private static void MapHttpAttributeRoutesInternal(this HttpRouteCollection rout
generatedRoutes.ForEach(r =>
{
var mvcRoute = mvcRoutes.MapHttpRoute(r.RouteName, r.Url, r.Defaults, r.Constraints, r.Handler);
+ mvcRoute.DataTokens = new RouteValueDictionary(r.DataTokens);
mvcRoute.RouteHandler = routeHandler;
});
}
diff --git a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
index 3d09d9d..72b09f1 100644
--- a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
@@ -25,6 +25,11 @@ public RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, Regex
return new RegexRouteConstraint(pattern, options);
}
+ public IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods)
+ {
+ return new RestfulHttpMethodConstraint(httpMethods);
+ }
+
public object CreateInlineRouteConstraint(string name, params object[] parameters)
{
var inlineRouteConstraints = _configuration.InlineRouteConstraints;
diff --git a/src/AttributeRouting.Web/AttributeRouting.Web.csproj b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
index cc59709..48c4ceb 100644
--- a/src/AttributeRouting.Web/AttributeRouting.Web.csproj
+++ b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
@@ -64,6 +64,7 @@
+
diff --git a/src/AttributeRouting.Web/Constraints/RestfulHttpMethodConstraint.cs b/src/AttributeRouting.Web/Constraints/RestfulHttpMethodConstraint.cs
new file mode 100644
index 0000000..6a28ff2
--- /dev/null
+++ b/src/AttributeRouting.Web/Constraints/RestfulHttpMethodConstraint.cs
@@ -0,0 +1,32 @@
+using System.Linq;
+using System.Web;
+using System.Web.Routing;
+using AttributeRouting.Constraints;
+using AttributeRouting.Helpers;
+using AttributeRouting.Web.Helpers;
+
+namespace AttributeRouting.Web.Constraints
+{
+ public class RestfulHttpMethodConstraint : HttpMethodConstraint, IRestfulHttpMethodConstraint
+ {
+ ///
+ /// Constrains a route by HTTP method.
+ ///
+ public RestfulHttpMethodConstraint(params string[] allowedMethods)
+ : base(allowedMethods)
+ {
+ }
+
+ protected override bool Match(HttpContextBase httpContext, Route route, string parameterName,
+ RouteValueDictionary values, RouteDirection routeDirection)
+ {
+
+ if (routeDirection == RouteDirection.UrlGeneration)
+ return true;
+
+ var httpMethod = httpContext.Request.GetHttpMethod();
+
+ return AllowedMethods.Any(m => m.ValueEquals(httpMethod));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs b/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs
index 5cad7ee..6e0f599 100644
--- a/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs
+++ b/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs
@@ -1,6 +1,7 @@
using System.Collections.Specialized;
using System.Reflection;
using System.Web;
+using AttributeRouting.Helpers;
namespace AttributeRouting.Web.Helpers
{
@@ -45,5 +46,13 @@ private static string GetUnvalidatedCollectionValue(this HttpRequestBase request
return null;
}
}
+
+ public static string GetHttpMethod(this HttpRequestBase request)
+ {
+ return request.SafeGet(r => r.Headers["X-HTTP-Method-Override"]) ??
+ request.SafeGet(r => GetFormValue(r, "X-HTTP-Method-Override")) ??
+ request.SafeGet(r => GetQueryStringValue(r, "X-HTTP-Method-Override")) ??
+ request.SafeGet(r => r.HttpMethod, "GET");
+ }
}
}
diff --git a/src/AttributeRouting/AttributeRouting.csproj b/src/AttributeRouting/AttributeRouting.csproj
index 0eda025..f7d136e 100644
--- a/src/AttributeRouting/AttributeRouting.csproj
+++ b/src/AttributeRouting/AttributeRouting.csproj
@@ -64,6 +64,7 @@
+
diff --git a/src/AttributeRouting/Constraints/IRestfulHttpMethodConstraint.cs b/src/AttributeRouting/Constraints/IRestfulHttpMethodConstraint.cs
new file mode 100644
index 0000000..db2a1d9
--- /dev/null
+++ b/src/AttributeRouting/Constraints/IRestfulHttpMethodConstraint.cs
@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using AttributeRouting.Framework.Factories;
+
+namespace AttributeRouting.Constraints
+{
+ ///
+ /// Abstraction used by
+ /// when applying restful route constraints.
+ ///
+ ///
+ /// Due to
+ /// System.Web.Routing.HttpMethodConstraint (used in web-hosted scenarios) and
+ /// System.Web.Http.Routing.HttpMethodConstraint (used in self-hosted scenarios).
+ ///
+ public interface IRestfulHttpMethodConstraint
+ {
+ ///
+ /// The allowed HTTP methods.
+ ///
+ ICollection AllowedMethods { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting/Framework/AttributeRouteExtensions.cs b/src/AttributeRouting/Framework/AttributeRouteExtensions.cs
index 7855fa5..0eb2780 100644
--- a/src/AttributeRouting/Framework/AttributeRouteExtensions.cs
+++ b/src/AttributeRouting/Framework/AttributeRouteExtensions.cs
@@ -134,9 +134,10 @@ public static TVirtualPathData GetVirtualPath(this IAttributeR
? ((IOptionalRouteConstraintWrapper)constraint).Constraint
: constraint;
- if (constraintToTest is IQueryStringRouteConstraintWrapper)
- queryStringConstraints.Add(constraintKey, constraint);
+ if (!(constraintToTest is IQueryStringRouteConstraintWrapper))
+ continue;
+ queryStringConstraints.Add(constraintKey, constraint);
route.Constraints.Remove(constraintKey);
}
diff --git a/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs b/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs
index db34075..708c0c4 100644
--- a/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs
+++ b/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs
@@ -19,6 +19,11 @@ public interface IRouteConstraintFactory
///
RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, RegexOptions options = RegexOptions.None);
+ ///
+ /// Creates a new RestfulHttpMethodConstraint
+ ///
+ IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods);
+
///
/// Creates an inline constraint of a specific type with the given parameters.
///
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 41b54a4..21b6ae3 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -196,6 +196,10 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
{
var constraints = new Dictionary();
+ // Default constraints
+ if (routeSpec.HttpMethods.Any())
+ constraints.Add("httpMethod", _routeConstraintFactory.CreateRestfulHttpMethodConstraint(routeSpec.HttpMethods));
+
// Work from a complete, tokenized url; ie: support constraints in area urls, route prefix urls, and route urls.
var tokenizedUrl = BuildTokenizedUrl(routeSpec.RouteUrl, routeSpec.RoutePrefixUrl, routeSpec.AreaUrl, routeSpec);
var urlParameters = GetUrlParameterContents(tokenizedUrl).ToList();
diff --git a/src/AttributeRouting/Logging/AttributeRouteInfo.cs b/src/AttributeRouting/Logging/AttributeRouteInfo.cs
index 08373fe..7e3f9d4 100644
--- a/src/AttributeRouting/Logging/AttributeRouteInfo.cs
+++ b/src/AttributeRouting/Logging/AttributeRouteInfo.cs
@@ -48,7 +48,7 @@ public static AttributeRouteInfo GetRouteInfo(string url,
{
foreach (var constraint in constraints)
{
- if (constraint.Value == null)
+ if (constraint.Value == null || constraint.Value is IRestfulHttpMethodConstraint)
continue;
if (constraint.Value is RegexRouteConstraintBase)
@@ -109,11 +109,11 @@ public static AttributeRouteInfo GetRouteInfo(string url,
{
if (token.Key.ValueEquals("namespaces"))
{
- item.DataTokens.Add(token.Key, ((string[]) token.Value).Aggregate((n1, n2) => n1 + ", " + n2));
+ item.DataTokens.Add(token.Key, String.Join(", ", (string[])token.Value));
}
else if (token.Key.ValueEquals("httpMethods"))
{
- item.HttpMethods = ((string[]) token.Value).Aggregate((n1, n2) => n1 + ", " + n2);
+ item.HttpMethods = String.Join(", ", (string[])token.Value);
}
else if (!token.Key.ValueEquals("actionMethod"))
{
From d4f81ba41364e867c8ba0f44cb12a6f3854beef4 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 11 Dec 2012 23:23:51 -0700
Subject: [PATCH 20/97] #146 - stopped checking for x-http-method-overrides
when constraing inbound routes by http method in asp.net web api.
---
.../Steps/StandardUsageSteps.cs | 2 +-
.../AttributeRouting.Web.Http.SelfHost.csproj | 2 +-
...aint.cs => InboundHttpMethodConstraint.cs} | 12 ++++----
.../Factories/RouteConstraintFactory.cs | 4 +--
.../AttributeRouting.Web.Http.WebHost.csproj | 1 +
.../InboundHttpMethodConstraint.cs | 26 +++++++++++++++++
.../Factories/RouteConstraintFactory.cs | 4 +--
.../AttributeRouting.Web.Mvc.csproj | 1 +
.../InboundHttpMethodConstraint.cs} | 14 +++++-----
.../Factories/RouteConstraintFactory.cs | 4 +--
.../RouteAttribute.cs | 5 ++--
.../AttributeRouting.Web.csproj | 1 -
.../Helpers/HttpRequestBaseExtensions.cs | 28 ++++++++++++++++---
src/AttributeRouting/AttributeRouting.csproj | 2 +-
...int.cs => IInboundHttpMethodConstraint.cs} | 4 +--
.../Factories/IRouteConstraintFactory.cs | 6 ++--
.../Framework/RouteBuilder.cs | 2 +-
.../Logging/AttributeRouteInfo.cs | 2 +-
18 files changed, 84 insertions(+), 36 deletions(-)
rename src/AttributeRouting.Web.Http.SelfHost/Constraints/{RestfulHttpMethodConstraint.cs => InboundHttpMethodConstraint.cs} (58%)
create mode 100644 src/AttributeRouting.Web.Http.WebHost/Constraints/InboundHttpMethodConstraint.cs
rename src/{AttributeRouting.Web/Constraints/RestfulHttpMethodConstraint.cs => AttributeRouting.Web.Mvc/Constraints/InboundHttpMethodConstraint.cs} (60%)
rename src/AttributeRouting/Constraints/{IRestfulHttpMethodConstraint.cs => IInboundHttpMethodConstraint.cs} (85%)
diff --git a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
index 8f0f9b3..920383c 100644
--- a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
+++ b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
@@ -81,7 +81,7 @@ public void ThenTheRouteForIsConstrainedToRequests(string action, string method)
private void AssertThatRouteIsConstrainedToHttpMethod(Route route, string method)
{
- var constraint = route.Constraints["httpMethod"] as IRestfulHttpMethodConstraint;
+ var constraint = route.Constraints["httpMethod"] as IInboundHttpMethodConstraint;
if (method.HasValue())
{
diff --git a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
index 4c584e0..495741c 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
+++ b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
@@ -68,7 +68,7 @@
SharedAssemblyInfo.cs
-
+
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs b/src/AttributeRouting.Web.Http.SelfHost/Constraints/InboundHttpMethodConstraint.cs
similarity index 58%
rename from src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs
rename to src/AttributeRouting.Web.Http.SelfHost/Constraints/InboundHttpMethodConstraint.cs
index 6d94f39..e4148ca 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs
+++ b/src/AttributeRouting.Web.Http.SelfHost/Constraints/InboundHttpMethodConstraint.cs
@@ -7,17 +7,17 @@
namespace AttributeRouting.Web.Http.SelfHost.Constraints
{
- ///
- /// Constrains a route by HTTP method.
- ///
- public class RestfulHttpMethodConstraint : HttpMethodConstraint, IRestfulHttpMethodConstraint
+ public class InboundHttpMethodConstraint : HttpMethodConstraint, IInboundHttpMethodConstraint
{
- public RestfulHttpMethodConstraint(params HttpMethod[] allowedMethods)
+ ///
+ /// Constrains an inbound route by HTTP method.
+ ///
+ public InboundHttpMethodConstraint(params HttpMethod[] allowedMethods)
: base(allowedMethods)
{
}
- ICollection IRestfulHttpMethodConstraint.AllowedMethods
+ ICollection IInboundHttpMethodConstraint.AllowedMethods
{
get { return new ReadOnlyCollection(AllowedMethods.Select(method => method.Method).ToList()); }
}
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
index 2133c36..f26e220 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
@@ -26,10 +26,10 @@ public RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, Regex
return new RegexRouteConstraint(pattern, options);
}
- public IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods)
+ public IInboundHttpMethodConstraint CreateInboundHttpMethodConstraint(string[] httpMethods)
{
var allowedMethods = httpMethods.Select(m => new HttpMethod(m)).ToArray();
- return new RestfulHttpMethodConstraint(allowedMethods);
+ return new InboundHttpMethodConstraint(allowedMethods);
}
public object CreateInlineRouteConstraint(string name, params object[] parameters)
diff --git a/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj b/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj
index c169336..ec02dee 100644
--- a/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj
+++ b/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj
@@ -72,6 +72,7 @@
SharedAssemblyInfo.cs
+
diff --git a/src/AttributeRouting.Web.Http.WebHost/Constraints/InboundHttpMethodConstraint.cs b/src/AttributeRouting.Web.Http.WebHost/Constraints/InboundHttpMethodConstraint.cs
new file mode 100644
index 0000000..e41288e
--- /dev/null
+++ b/src/AttributeRouting.Web.Http.WebHost/Constraints/InboundHttpMethodConstraint.cs
@@ -0,0 +1,26 @@
+using System.Web;
+using System.Web.Routing;
+using AttributeRouting.Constraints;
+
+namespace AttributeRouting.Web.Http.WebHost.Constraints
+{
+ public class InboundHttpMethodConstraint : HttpMethodConstraint, IInboundHttpMethodConstraint
+ {
+ ///
+ /// Constrains an inbound route by HTTP method.
+ ///
+ public InboundHttpMethodConstraint(params string[] allowedMethods)
+ : base(allowedMethods)
+ {
+ }
+
+ protected override bool Match(HttpContextBase httpContext, Route route, string parameterName,
+ RouteValueDictionary values, RouteDirection routeDirection)
+ {
+ if (routeDirection == RouteDirection.UrlGeneration)
+ return true;
+
+ return base.Match(httpContext, route, parameterName, values, routeDirection);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
index 50f5f98..a004ddd 100644
--- a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
@@ -25,9 +25,9 @@ public RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, Regex
return new RegexRouteConstraint(pattern, options);
}
- public IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods)
+ public IInboundHttpMethodConstraint CreateInboundHttpMethodConstraint(string[] httpMethods)
{
- return new RestfulHttpMethodConstraint(httpMethods);
+ return new InboundHttpMethodConstraint(httpMethods);
}
public object CreateInlineRouteConstraint(string name, params object[] parameters)
diff --git a/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj b/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
index 79a64f4..a67c256 100644
--- a/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
+++ b/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
@@ -54,6 +54,7 @@
SharedAssemblyInfo.cs
+
diff --git a/src/AttributeRouting.Web/Constraints/RestfulHttpMethodConstraint.cs b/src/AttributeRouting.Web.Mvc/Constraints/InboundHttpMethodConstraint.cs
similarity index 60%
rename from src/AttributeRouting.Web/Constraints/RestfulHttpMethodConstraint.cs
rename to src/AttributeRouting.Web.Mvc/Constraints/InboundHttpMethodConstraint.cs
index 6a28ff2..0b48fd3 100644
--- a/src/AttributeRouting.Web/Constraints/RestfulHttpMethodConstraint.cs
+++ b/src/AttributeRouting.Web.Mvc/Constraints/InboundHttpMethodConstraint.cs
@@ -1,18 +1,18 @@
using System.Linq;
using System.Web;
+using System.Web.Mvc;
using System.Web.Routing;
using AttributeRouting.Constraints;
using AttributeRouting.Helpers;
-using AttributeRouting.Web.Helpers;
-namespace AttributeRouting.Web.Constraints
+namespace AttributeRouting.Web.Mvc.Constraints
{
- public class RestfulHttpMethodConstraint : HttpMethodConstraint, IRestfulHttpMethodConstraint
+ public class InboundHttpMethodConstraint : HttpMethodConstraint, IInboundHttpMethodConstraint
{
///
- /// Constrains a route by HTTP method.
+ /// Constrains an inbound route by HTTP method.
///
- public RestfulHttpMethodConstraint(params string[] allowedMethods)
+ public InboundHttpMethodConstraint(params string[] allowedMethods)
: base(allowedMethods)
{
}
@@ -20,11 +20,11 @@ public RestfulHttpMethodConstraint(params string[] allowedMethods)
protected override bool Match(HttpContextBase httpContext, Route route, string parameterName,
RouteValueDictionary values, RouteDirection routeDirection)
{
-
if (routeDirection == RouteDirection.UrlGeneration)
return true;
- var httpMethod = httpContext.Request.GetHttpMethod();
+ // Make sure to check for HTTP method overrides!
+ var httpMethod = httpContext.Request.GetHttpMethodOverride();
return AllowedMethods.Any(m => m.ValueEquals(httpMethod));
}
diff --git a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
index 72b09f1..ac2cff7 100644
--- a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
@@ -25,9 +25,9 @@ public RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, Regex
return new RegexRouteConstraint(pattern, options);
}
- public IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods)
+ public IInboundHttpMethodConstraint CreateInboundHttpMethodConstraint(string[] httpMethods)
{
- return new RestfulHttpMethodConstraint(httpMethods);
+ return new InboundHttpMethodConstraint(httpMethods);
}
public object CreateInlineRouteConstraint(string name, params object[] parameters)
diff --git a/src/AttributeRouting.Web.Mvc/RouteAttribute.cs b/src/AttributeRouting.Web.Mvc/RouteAttribute.cs
index edcece5..7f73314 100644
--- a/src/AttributeRouting.Web.Mvc/RouteAttribute.cs
+++ b/src/AttributeRouting.Web.Mvc/RouteAttribute.cs
@@ -124,12 +124,13 @@ public override bool IsValidForRequest(ControllerContext controllerContext, Meth
{
if (controllerContext == null) throw new ArgumentNullException("controllerContext");
+ // If not constrained by a method, then accept always!
if (!HttpMethods.Any())
return true;
- var method = controllerContext.HttpContext.Request.GetHttpMethodOverride();
+ var httpMethod = controllerContext.HttpContext.Request.GetHttpMethodOverride();
- return HttpMethods.Any(m => m.ValueEquals(method));
+ return HttpMethods.Any(m => m.ValueEquals(httpMethod));
}
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web/AttributeRouting.Web.csproj b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
index 48c4ceb..cc59709 100644
--- a/src/AttributeRouting.Web/AttributeRouting.Web.csproj
+++ b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
@@ -64,7 +64,6 @@
-
diff --git a/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs b/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs
index 6e0f599..58e5ed5 100644
--- a/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs
+++ b/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs
@@ -47,12 +47,32 @@ private static string GetUnvalidatedCollectionValue(this HttpRequestBase request
}
}
+ ///
+ /// The reason we have our own is to provide support for System.Web.Helpers.Validation.Unvalidated calls.
+ ///
public static string GetHttpMethod(this HttpRequestBase request)
{
- return request.SafeGet(r => r.Headers["X-HTTP-Method-Override"]) ??
- request.SafeGet(r => GetFormValue(r, "X-HTTP-Method-Override")) ??
- request.SafeGet(r => GetQueryStringValue(r, "X-HTTP-Method-Override")) ??
- request.SafeGet(r => r.HttpMethod, "GET");
+ var httpMethod = request.HttpMethod;
+
+ // If not a post, method overrides don't apply.
+ if (!httpMethod.ValueEquals("POST"))
+ {
+ return httpMethod;
+ }
+
+ // Get the method override and if it's not for a GET or POST, then apply it.
+ var methodOverride = request.SafeGet(r => r.Headers["X-HTTP-Method-Override"]) ??
+ request.SafeGet(r => GetFormValue(r, "X-HTTP-Method-Override")) ??
+ request.SafeGet(r => GetQueryStringValue(r, "X-HTTP-Method-Override"));
+
+ if (methodOverride != null &&
+ (!methodOverride.ValueEquals("GET") && !methodOverride.ValueEquals("POST")))
+ {
+ return methodOverride;
+ }
+
+ // Otherwise, just return the http method.
+ return httpMethod;
}
}
}
diff --git a/src/AttributeRouting/AttributeRouting.csproj b/src/AttributeRouting/AttributeRouting.csproj
index f7d136e..0deaa0c 100644
--- a/src/AttributeRouting/AttributeRouting.csproj
+++ b/src/AttributeRouting/AttributeRouting.csproj
@@ -64,7 +64,7 @@
-
+
diff --git a/src/AttributeRouting/Constraints/IRestfulHttpMethodConstraint.cs b/src/AttributeRouting/Constraints/IInboundHttpMethodConstraint.cs
similarity index 85%
rename from src/AttributeRouting/Constraints/IRestfulHttpMethodConstraint.cs
rename to src/AttributeRouting/Constraints/IInboundHttpMethodConstraint.cs
index db2a1d9..be1b849 100644
--- a/src/AttributeRouting/Constraints/IRestfulHttpMethodConstraint.cs
+++ b/src/AttributeRouting/Constraints/IInboundHttpMethodConstraint.cs
@@ -5,14 +5,14 @@ namespace AttributeRouting.Constraints
{
///
/// Abstraction used by
- /// when applying restful route constraints.
+ /// when applying inbound http method constraints.
///
///
/// Due to
/// System.Web.Routing.HttpMethodConstraint (used in web-hosted scenarios) and
/// System.Web.Http.Routing.HttpMethodConstraint (used in self-hosted scenarios).
///
- public interface IRestfulHttpMethodConstraint
+ public interface IInboundHttpMethodConstraint
{
///
/// The allowed HTTP methods.
diff --git a/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs b/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs
index 708c0c4..c2cbb15 100644
--- a/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs
+++ b/src/AttributeRouting/Framework/Factories/IRouteConstraintFactory.cs
@@ -15,14 +15,14 @@ namespace AttributeRouting.Framework.Factories
public interface IRouteConstraintFactory
{
///
- /// Creates a new RegexRouteConstraint
+ /// Creates a new regex route constraint.
///
RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, RegexOptions options = RegexOptions.None);
///
- /// Creates a new RestfulHttpMethodConstraint
+ /// Creates a new inbound http method constraint.
///
- IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods);
+ IInboundHttpMethodConstraint CreateInboundHttpMethodConstraint(string[] httpMethods);
///
/// Creates an inline constraint of a specific type with the given parameters.
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 21b6ae3..06e9702 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -198,7 +198,7 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
// Default constraints
if (routeSpec.HttpMethods.Any())
- constraints.Add("httpMethod", _routeConstraintFactory.CreateRestfulHttpMethodConstraint(routeSpec.HttpMethods));
+ constraints.Add("inboundHttpMethod", _routeConstraintFactory.CreateInboundHttpMethodConstraint(routeSpec.HttpMethods));
// Work from a complete, tokenized url; ie: support constraints in area urls, route prefix urls, and route urls.
var tokenizedUrl = BuildTokenizedUrl(routeSpec.RouteUrl, routeSpec.RoutePrefixUrl, routeSpec.AreaUrl, routeSpec);
diff --git a/src/AttributeRouting/Logging/AttributeRouteInfo.cs b/src/AttributeRouting/Logging/AttributeRouteInfo.cs
index 7e3f9d4..a6141b9 100644
--- a/src/AttributeRouting/Logging/AttributeRouteInfo.cs
+++ b/src/AttributeRouting/Logging/AttributeRouteInfo.cs
@@ -48,7 +48,7 @@ public static AttributeRouteInfo GetRouteInfo(string url,
{
foreach (var constraint in constraints)
{
- if (constraint.Value == null || constraint.Value is IRestfulHttpMethodConstraint)
+ if (constraint.Value == null || constraint.Value is IInboundHttpMethodConstraint)
continue;
if (constraint.Value is RegexRouteConstraintBase)
From 3e0cfc6dd04c98bafe6e19ec49ec4fe73a99aabf Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 11 Dec 2012 23:25:58 -0700
Subject: [PATCH 21/97] fixed broken test execution.
---
src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
index 920383c..c68d4d4 100644
--- a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
+++ b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
@@ -81,7 +81,7 @@ public void ThenTheRouteForIsConstrainedToRequests(string action, string method)
private void AssertThatRouteIsConstrainedToHttpMethod(Route route, string method)
{
- var constraint = route.Constraints["httpMethod"] as IInboundHttpMethodConstraint;
+ var constraint = route.Constraints["inboundHttpMethod"] as IInboundHttpMethodConstraint;
if (method.HasValue())
{
From 955b0454e3762d254181b48aa37b96980594271e Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 11 Dec 2012 23:49:33 -0700
Subject: [PATCH 22/97] #162 - opened up {controller} and {action} url params
as special params that will be replaced with controller and action values
from the defaults collection for the route.
---
.../Features/RouteDefaults.feature | 6 ++++
.../Features/RouteDefaults.feature.cs | 27 ++++++++++++--
.../Http/HttpRouteDefaultsController.cs | 6 ++++
.../Subjects/RouteDefaultsController.cs | 6 ++++
.../Framework/RouteBuilder.cs | 36 +++++++++++--------
5 files changed, 63 insertions(+), 18 deletions(-)
diff --git a/src/AttributeRouting.Specs/Features/RouteDefaults.feature b/src/AttributeRouting.Specs/Features/RouteDefaults.feature
index 490d2c4..be34fe1 100644
--- a/src/AttributeRouting.Specs/Features/RouteDefaults.feature
+++ b/src/AttributeRouting.Specs/Features/RouteDefaults.feature
@@ -23,3 +23,9 @@ Scenario: Optional parameters specified with a url parameter token
When I fetch the routes for the HttpRouteDefaults controller's Optionals action
Then the route url is "Optionals/{p1}"
And the parameter "p1" is optional
+
+Scenario: Using the controller and action url params
+ When I fetch the routes for the RouteDefaults controller's TheActionName action
+ Then the route url is "RouteDefaults/TheActionName"
+ When I fetch the routes for the HttpRouteDefaults controller's TheActionName action
+ Then the route url is "HttpRouteDefaults/TheActionName"
diff --git a/src/AttributeRouting.Specs/Features/RouteDefaults.feature.cs b/src/AttributeRouting.Specs/Features/RouteDefaults.feature.cs
index ce10e38..e7552b2 100644
--- a/src/AttributeRouting.Specs/Features/RouteDefaults.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteDefaults.feature.cs
@@ -1,9 +1,9 @@
// ------------------------------------------------------------------------------
//
// This code was generated by SpecFlow (http://www.specflow.org/).
-// SpecFlow Version:1.9.1.84
+// SpecFlow Version:1.9.0.77
// SpecFlow Generator Version:1.9.0.0
-// Runtime Version:4.0.30319.17929
+// Runtime Version:4.0.30319.18010
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -16,7 +16,7 @@ namespace AttributeRouting.Specs.Features
using TechTalk.SpecFlow;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.1.84")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Route Defaults")]
@@ -128,6 +128,27 @@ public virtual void OptionalParametersSpecifiedWithAUrlParameterToken()
testRunner.Then("the route url is \"Optionals/{p1}\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 25
testRunner.And("the parameter \"p1\" is optional", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Using the controller and action url params")]
+ public virtual void UsingTheControllerAndActionUrlParams()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Using the controller and action url params", ((string[])(null)));
+#line 27
+this.ScenarioSetup(scenarioInfo);
+#line 3
+this.FeatureBackground();
+#line 28
+ testRunner.When("I fetch the routes for the RouteDefaults controller\'s TheActionName action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 29
+ testRunner.Then("the route url is \"RouteDefaults/TheActionName\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 30
+ testRunner.When("I fetch the routes for the HttpRouteDefaults controller\'s TheActionName action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 31
+ testRunner.Then("the route url is \"HttpRouteDefaults/TheActionName\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Subjects/Http/HttpRouteDefaultsController.cs b/src/AttributeRouting.Specs/Subjects/Http/HttpRouteDefaultsController.cs
index ba31baf..7223410 100644
--- a/src/AttributeRouting.Specs/Subjects/Http/HttpRouteDefaultsController.cs
+++ b/src/AttributeRouting.Specs/Subjects/Http/HttpRouteDefaultsController.cs
@@ -16,5 +16,11 @@ public string Optionals(string p1)
{
return "";
}
+
+ [GET("{controller}/{action}")]
+ public string TheActionName()
+ {
+ return "is jack";
+ }
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Specs/Subjects/RouteDefaultsController.cs b/src/AttributeRouting.Specs/Subjects/RouteDefaultsController.cs
index f1ef0a5..e79638e 100644
--- a/src/AttributeRouting.Specs/Subjects/RouteDefaultsController.cs
+++ b/src/AttributeRouting.Specs/Subjects/RouteDefaultsController.cs
@@ -16,5 +16,11 @@ public ActionResult Optionals(string p1)
{
return Content("");
}
+
+ [GET("{controller}/{action}")]
+ public string TheActionName()
+ {
+ return "is joe";
+ }
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 06e9702..d8f62ca 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -53,10 +53,12 @@ where s.Subdomain.HasValue()
private IEnumerable Build(RouteSpecification routeSpec)
{
- var routes = _routeFactory.CreateAttributeRoutes(CreateRouteUrl(routeSpec),
- CreateRouteDefaults(routeSpec),
- CreateRouteConstraints(routeSpec),
- CreateRouteDataTokens(routeSpec));
+ var defaults = CreateRouteDefaults(routeSpec);
+ var constraints = CreateRouteConstraints(routeSpec);
+ var dataTokens = CreateRouteDataTokens(routeSpec);
+ var url = CreateRouteUrl(defaults, routeSpec);
+
+ var routes = _routeFactory.CreateAttributeRoutes(url, defaults, constraints, dataTokens);
foreach (var route in routes)
{
@@ -98,15 +100,16 @@ private string CreateRouteName(RouteSpecification routeSpec)
return _configuration.AutoGenerateRouteNames ? _configuration.RouteNameBuilder(routeSpec) : null;
}
- private string CreateRouteUrl(RouteSpecification routeSpec)
+ private string CreateRouteUrl(IDictionary defaults, RouteSpecification routeSpec)
{
return CreateRouteUrl(routeSpec.RouteUrl,
routeSpec.RoutePrefixUrl,
routeSpec.AreaUrl,
+ defaults,
routeSpec);
}
- private string CreateRouteUrl(string routeUrl, string routePrefix, string areaUrl, RouteSpecification routeSpec)
+ private string CreateRouteUrl(string routeUrl, string routePrefix, string areaUrl, IDictionary defaults, RouteSpecification routeSpec)
{
var tokenizedUrl = BuildTokenizedUrl(routeUrl, routePrefix, areaUrl, routeSpec);
var tokenizedPath = RemoveQueryString(tokenizedUrl);
@@ -114,19 +117,21 @@ private string CreateRouteUrl(string routeUrl, string routePrefix, string areaUr
var urlParameterNames = GetUrlParameterContents(detokenizedPath).ToList();
- // {controller} and {action} tokens are not valid
+ var urlBuilder = new StringBuilder(detokenizedPath);
+
+ // Replace {controller} URL param with default value.
if (urlParameterNames.Any(n => n.ValueEquals("controller")))
- throw new AttributeRoutingException("{controller} is not a valid url parameter.");
+ urlBuilder.Replace("{controller}", (string)defaults["controller"]);
+
+ // Replace {action} URL param with default value.
if (urlParameterNames.Any(n => n.ValueEquals("action")))
- throw new AttributeRoutingException("{action} is not a valid url parameter.");
+ urlBuilder.Replace("{action}", (string)defaults["action"]);
// Explicitly defined area routes are not valid
if (urlParameterNames.Any(n => n.ValueEquals("area")))
throw new AttributeRoutingException(
"{area} url parameters are not allowed. Specify the area name by using the RouteAreaAttribute.");
- var urlBuilder = new StringBuilder(detokenizedPath);
-
// If we are lowercasing routes, then lowercase everything but the route params
var lower = routeSpec.UseLowercaseRoute.GetValueOrDefault(_configuration.UseLowercaseRoutes);
if (lower)
@@ -431,15 +436,16 @@ private IEnumerable CreateRouteTranslations(RouteSpecification
//*********************************************
// Otherwise, build a translated route
+ var defaults = CreateRouteDefaults(routeSpec);
+ var constraints = CreateRouteConstraints(routeSpec);
+ var dataTokens = CreateRouteDataTokens(routeSpec);
var routeUrl = CreateRouteUrl(translatedRouteUrl ?? routeSpec.RouteUrl,
translatedRoutePrefix ?? routeSpec.RoutePrefixUrl,
translatedAreaUrl ?? routeSpec.AreaUrl,
+ defaults,
routeSpec);
- var translatedRoutes = _routeFactory.CreateAttributeRoutes(routeUrl,
- CreateRouteDefaults(routeSpec),
- CreateRouteConstraints(routeSpec),
- CreateRouteDataTokens(routeSpec));
+ var translatedRoutes = _routeFactory.CreateAttributeRoutes(routeUrl, defaults, constraints, dataTokens);
foreach (var translatedRoute in translatedRoutes)
{
From 2a24eaa88c2765f06c84d254a54260eb8cf83663 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Wed, 12 Dec 2012 00:03:04 -0700
Subject: [PATCH 23/97] Updated reame and assembly info for 3.4
---
README.textile | 11 +++++++++++
src/SharedAssemblyInfo.cs | 6 +++---
2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/README.textile b/README.textile
index f091f4d..3ec8bd1 100644
--- a/README.textile
+++ b/README.textile
@@ -4,6 +4,17 @@ h2. Please refer to "attributerouting.net":http://attributerouting.net/ for docu
h3. Changelog
+_3.4_
+
+* #124 - Now supporting custom IRouteHandler in web-host scenario. Also supporting custom HttpMessageHandler for Web API.
+* #146 - BUG FIX: AR's custom http method constraint was interfering with IHostBufferPolicySelector, because it was checking for the incoming http method too early in the pipeline.
+* #155 - Can now specify multiple route prefixes on a controller. Every action will get each prefix. You can control precedence of the prefixes by using the Precedence property.
+* #156 - Added action method to route data tokens.
+* #161 - BUG FIX: Url generation was bonking for routes that included a querystring route param constraint.
+* #162 - Modified design of route convention attribute, adding facility for specifying an area attribute for a controller. Also opened up {controller} and {action} url params as special params that will be replaced with controller and action values from the defaults collection for the route.
+* #164 - Added default ctors to RoutePrefixAttribute, RouteAreaAttribute, and the AttributeRouting.Web.Mvc route attributes. These default ctors will use a convention to get their URL components: RoutePrefix - will use the controller name; RouteArea - will use the last section of the controller's namespace; and the route attributes - will use the action name by default.
+* #165 - Added two flags to the route attributes: IgnoreRoutePrefix and IgnoreAreaUrl. These flags control whether to prepend route prefixes or area urls to the generated route url.
+
_3.3_
* #153 - fixed bad parsing of regex route constraint patterns that use a comma.
diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs
index 8a3e3f9..2a41a79 100644
--- a/src/SharedAssemblyInfo.cs
+++ b/src/SharedAssemblyInfo.cs
@@ -7,7 +7,7 @@
[assembly: AssemblyProduct("AttributeRouting")]
[assembly: AssemblyCopyright("Copyright 2010-2012 Tim McCall")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("3.3")]
-[assembly: AssemblyFileVersion("3.3")]
-[assembly: AssemblyInformationalVersion("3.3")]
+[assembly: AssemblyVersion("3.4")]
+[assembly: AssemblyFileVersion("3.4")]
+[assembly: AssemblyInformationalVersion("3.4")]
[assembly: AssemblyConfiguration("Release")]
From f253275408747131dfb8ddfe3344a273c930b0ec Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Wed, 19 Dec 2012 18:02:20 -0700
Subject: [PATCH 24/97] tersed the names of the config objects.
---
.../Steps/SharedSteps.cs | 8 +-
.../Tests/Subdomains/RouteReflectorTests.cs | 8 +-
.../TrailingSlashes/AttributeRouteTests.cs | 2 +-
.../AttributeRouting.Web.Http.SelfHost.csproj | 2 +-
.../Factories/AttributeRouteFactory.cs | 4 +-
.../Factories/RouteConstraintFactory.cs | 4 +-
...gConfiguration.cs => HttpConfiguration.cs} | 68 +--
.../HttpRouteCollectionExtensions.cs | 10 +-
.../AttributeRouting.Web.Http.WebHost.csproj | 2 +-
.../Factories/AttributeRouteFactory.cs | 4 +-
.../Factories/RouteConstraintFactory.cs | 4 +-
.../HttpRouteCollectionExtensions.cs | 10 +-
...nfiguration.cs => HttpWebConfiguration.cs} | 84 +--
.../AttributeRouting.Web.Http.csproj | 2 +-
.../Framework/HttpAttributeRoute.cs | 4 +-
...rationBase.cs => HttpConfigurationBase.cs} | 4 +-
.../AttributeRouting.Web.Mvc.csproj | 2 +-
...utingConfiguration.cs => Configuration.cs} | 160 ++---
.../Framework/AttributeRoute.cs | 4 +-
.../Factories/AttributeRouteFactory.cs | 4 +-
.../Factories/RouteConstraintFactory.cs | 4 +-
.../RouteCollectionExtensions.cs | 10 +-
src/AttributeRouting/AreaConfiguration.cs | 4 +-
src/AttributeRouting/AttributeRouting.csproj | 2 +-
...figurationBase.cs => ConfigurationBase.cs} | 562 +++++++++---------
.../Framework/AttributeRouteExtensions.cs | 6 +-
.../Framework/IAttributeRoute.cs | 6 +-
.../Framework/RouteBuilder.cs | 6 +-
.../Framework/RouteReflector.cs | 6 +-
src/AttributeRouting/IRouteAttribute.cs | 6 +-
30 files changed, 501 insertions(+), 501 deletions(-)
rename src/AttributeRouting.Web.Http.SelfHost/{HttpAttributeRoutingConfiguration.cs => HttpConfiguration.cs} (88%)
rename src/AttributeRouting.Web.Http.WebHost/{HttpWebAttributeRoutingConfiguration.cs => HttpWebConfiguration.cs} (89%)
rename src/AttributeRouting.Web.Http/{HttpAttributeRoutingConfigurationBase.cs => HttpConfigurationBase.cs} (92%)
rename src/AttributeRouting.Web.Mvc/{AttributeRoutingConfiguration.cs => Configuration.cs} (93%)
rename src/AttributeRouting/{AttributeRoutingConfigurationBase.cs => ConfigurationBase.cs} (96%)
diff --git a/src/AttributeRouting.Specs/Steps/SharedSteps.cs b/src/AttributeRouting.Specs/Steps/SharedSteps.cs
index beb65a1..318b996 100644
--- a/src/AttributeRouting.Specs/Steps/SharedSteps.cs
+++ b/src/AttributeRouting.Specs/Steps/SharedSteps.cs
@@ -21,8 +21,8 @@ namespace AttributeRouting.Specs.Steps
[Binding]
public class SharedSteps
{
- private AttributeRoutingConfiguration _configuration;
- private HttpWebAttributeRoutingConfiguration _httpConfiguration;
+ private Configuration _configuration;
+ private HttpWebConfiguration _httpConfiguration;
[Given(@"I generate the routes defined in the subject controllers")]
public void GivenIGenerateTheRoutesDefinedInTheSubjectControllers()
@@ -74,10 +74,10 @@ public void GivenIHaveRegisteredTheRoutesForThe(string controllerName)
[Given(@"I have a new configuration object")]
public void GivenIHaveANewConfigurationObject()
{
- _configuration = new AttributeRoutingConfiguration();
+ _configuration = new Configuration();
_configuration.InlineRouteConstraints.Add("color", typeof(EnumRouteConstraint));
- _httpConfiguration = new HttpWebAttributeRoutingConfiguration();
+ _httpConfiguration = new HttpWebConfiguration();
_httpConfiguration.InlineRouteConstraints.Add("color", typeof(EnumRouteConstraint));
}
diff --git a/src/AttributeRouting.Specs/Tests/Subdomains/RouteReflectorTests.cs b/src/AttributeRouting.Specs/Tests/Subdomains/RouteReflectorTests.cs
index d26ee00..ee11de3 100644
--- a/src/AttributeRouting.Specs/Tests/Subdomains/RouteReflectorTests.cs
+++ b/src/AttributeRouting.Specs/Tests/Subdomains/RouteReflectorTests.cs
@@ -12,7 +12,7 @@ public class RouteReflectorTests
[Test]
public void Returns_null_for_area_url_when_subdomain_is_specified_and_area_url_is_not_specified()
{
- var configuration = new AttributeRoutingConfiguration();
+ var configuration = new Configuration();
configuration.AddRoutesFromController();
var reflector = new RouteReflector(configuration);
@@ -26,7 +26,7 @@ public void Returns_null_for_area_url_when_subdomain_is_specified_and_area_url_i
[Test]
public void Returns_specified_url_for_area_url_when_both_subdomain_is_specified_and_area_url_is_specified()
{
- var configuration = new AttributeRoutingConfiguration();
+ var configuration = new Configuration();
configuration.AddRoutesFromController();
var reflector = new RouteReflector(configuration);
@@ -40,7 +40,7 @@ public void Returns_specified_url_for_area_url_when_both_subdomain_is_specified_
[Test]
public void Returns_subdomain_specified_for_area_via_configuration_object()
{
- var configuration = new AttributeRoutingConfiguration();
+ var configuration = new Configuration();
configuration.AddRoutesFromController();
configuration.MapArea("Users").ToSubdomain("override");
@@ -57,7 +57,7 @@ public void Returns_subdomain_specified_for_area_via_configuration_object()
[Test]
public void Returns_null_area_url_when_controller_configured_with_subdomain_only_via_configuration_object()
{
- var configuration = new AttributeRoutingConfiguration();
+ var configuration = new Configuration();
configuration.AddRoutesFromController();
configuration.MapArea("NoSubdomain").ToSubdomain("subdomain");
diff --git a/src/AttributeRouting.Specs/Tests/TrailingSlashes/AttributeRouteTests.cs b/src/AttributeRouting.Specs/Tests/TrailingSlashes/AttributeRouteTests.cs
index 71910a0..4e4576f 100644
--- a/src/AttributeRouting.Specs/Tests/TrailingSlashes/AttributeRouteTests.cs
+++ b/src/AttributeRouting.Specs/Tests/TrailingSlashes/AttributeRouteTests.cs
@@ -115,7 +115,7 @@ public void It_does_not_append_trailing_slash_to_root_urls_when_configured_for_t
private Route BuildAttributeRoute(string url, bool useLowercaseRoutes, bool appendTrailingSlash)
{
- var configuration = new AttributeRoutingConfiguration
+ var configuration = new Configuration
{
UseLowercaseRoutes = useLowercaseRoutes,
AppendTrailingSlash = appendTrailingSlash,
diff --git a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
index 495741c..f404f8a 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
+++ b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
@@ -69,7 +69,7 @@
-
+
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/AttributeRouteFactory.cs b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/AttributeRouteFactory.cs
index f3c5fd5..dc11f59 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/AttributeRouteFactory.cs
+++ b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/AttributeRouteFactory.cs
@@ -8,9 +8,9 @@ namespace AttributeRouting.Web.Http.SelfHost.Framework.Factories
{
internal class AttributeRouteFactory : IAttributeRouteFactory
{
- private readonly HttpAttributeRoutingConfiguration _configuration;
+ private readonly HttpConfiguration _configuration;
- public AttributeRouteFactory(HttpAttributeRoutingConfiguration configuration)
+ public AttributeRouteFactory(HttpConfiguration configuration)
{
_configuration = configuration;
}
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
index f26e220..668df50 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs
@@ -14,9 +14,9 @@ namespace AttributeRouting.Web.Http.SelfHost.Framework.Factories
{
public class RouteConstraintFactory : IRouteConstraintFactory
{
- private readonly HttpAttributeRoutingConfiguration _configuration;
+ private readonly HttpConfiguration _configuration;
- public RouteConstraintFactory(HttpAttributeRoutingConfiguration configuration)
+ public RouteConstraintFactory(HttpConfiguration configuration)
{
_configuration = configuration;
}
diff --git a/src/AttributeRouting.Web.Http.SelfHost/HttpAttributeRoutingConfiguration.cs b/src/AttributeRouting.Web.Http.SelfHost/HttpConfiguration.cs
similarity index 88%
rename from src/AttributeRouting.Web.Http.SelfHost/HttpAttributeRoutingConfiguration.cs
rename to src/AttributeRouting.Web.Http.SelfHost/HttpConfiguration.cs
index 2afee9b..5840642 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/HttpAttributeRoutingConfiguration.cs
+++ b/src/AttributeRouting.Web.Http.SelfHost/HttpConfiguration.cs
@@ -1,34 +1,34 @@
-using System.Web.Http.Routing;
-using AttributeRouting.Framework;
-using AttributeRouting.Web.Http.Constraints;
-using AttributeRouting.Web.Http.SelfHost.Framework.Factories;
-
-namespace AttributeRouting.Web.Http.SelfHost
-{
- public class HttpAttributeRoutingConfiguration : HttpAttributeRoutingConfigurationBase
- {
- public HttpAttributeRoutingConfiguration()
- {
- AttributeRouteFactory = new AttributeRouteFactory(this);
- RouteConstraintFactory = new RouteConstraintFactory(this);
- ParameterFactory = new RouteParameterFactory();
-
- RegisterDefaultInlineRouteConstraints(typeof(RegexRouteConstraint).Assembly);
-
- // Must turn on AutoGenerateRouteNames and use the Unique RouteNameBuilder for this to work out-of-the-box.
- AutoGenerateRouteNames = true;
- RouteNameBuilder = RouteNameBuilders.Unique;
- }
-
- ///
- /// Automatically applies the specified constaint against url parameters
- /// with names that match the given regular expression.
- ///
- /// The regex used to match url parameter names
- /// The constraint to apply to matched parameters
- public void AddDefaultRouteConstraint(string keyRegex, IHttpRouteConstraint constraint)
- {
- base.AddDefaultRouteConstraint(keyRegex, constraint);
- }
- }
-}
+using System.Web.Http.Routing;
+using AttributeRouting.Framework;
+using AttributeRouting.Web.Http.Constraints;
+using AttributeRouting.Web.Http.SelfHost.Framework.Factories;
+
+namespace AttributeRouting.Web.Http.SelfHost
+{
+ public class HttpConfiguration : HttpConfigurationBase
+ {
+ public HttpConfiguration()
+ {
+ AttributeRouteFactory = new AttributeRouteFactory(this);
+ RouteConstraintFactory = new RouteConstraintFactory(this);
+ ParameterFactory = new RouteParameterFactory();
+
+ RegisterDefaultInlineRouteConstraints(typeof(RegexRouteConstraint).Assembly);
+
+ // Must turn on AutoGenerateRouteNames and use the Unique RouteNameBuilder for this to work out-of-the-box.
+ AutoGenerateRouteNames = true;
+ RouteNameBuilder = RouteNameBuilders.Unique;
+ }
+
+ ///
+ /// Automatically applies the specified constaint against url parameters
+ /// with names that match the given regular expression.
+ ///
+ /// The regex used to match url parameter names
+ /// The constraint to apply to matched parameters
+ public void AddDefaultRouteConstraint(string keyRegex, IHttpRouteConstraint constraint)
+ {
+ base.AddDefaultRouteConstraint(keyRegex, constraint);
+ }
+ }
+}
diff --git a/src/AttributeRouting.Web.Http.SelfHost/HttpRouteCollectionExtensions.cs b/src/AttributeRouting.Web.Http.SelfHost/HttpRouteCollectionExtensions.cs
index 8287df6..6ae3307 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/HttpRouteCollectionExtensions.cs
+++ b/src/AttributeRouting.Web.Http.SelfHost/HttpRouteCollectionExtensions.cs
@@ -18,7 +18,7 @@ public static class HttpRouteCollectionExtensions
///
public static void MapHttpAttributeRoutes(this HttpRouteCollection routes)
{
- var configuration = new HttpAttributeRoutingConfiguration();
+ var configuration = new HttpConfiguration();
configuration.AddRoutesFromAssembly(Assembly.GetCallingAssembly());
routes.MapHttpAttributeRoutesInternal(configuration);
@@ -30,9 +30,9 @@ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes)
///
///
/// The initialization action that builds the configuration object
- public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, Action configurationAction)
+ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, Action configurationAction)
{
- var configuration = new HttpAttributeRoutingConfiguration();
+ var configuration = new HttpConfiguration();
configurationAction.Invoke(configuration);
routes.MapHttpAttributeRoutesInternal(configuration);
@@ -44,12 +44,12 @@ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, Actio
///
///
/// The configuration object
- public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, HttpAttributeRoutingConfiguration configuration)
+ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, HttpConfiguration configuration)
{
routes.MapHttpAttributeRoutesInternal(configuration);
}
- private static void MapHttpAttributeRoutesInternal(this HttpRouteCollection routes, HttpAttributeRoutingConfiguration configuration)
+ private static void MapHttpAttributeRoutesInternal(this HttpRouteCollection routes, HttpConfiguration configuration)
{
var generatedRoutes = new RouteBuilder(configuration).BuildAllRoutes();
diff --git a/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj b/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj
index ec02dee..171927e 100644
--- a/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj
+++ b/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj
@@ -76,7 +76,7 @@
-
+
diff --git a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/AttributeRouteFactory.cs b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/AttributeRouteFactory.cs
index 47764cd..f867d28 100644
--- a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/AttributeRouteFactory.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/AttributeRouteFactory.cs
@@ -8,9 +8,9 @@ namespace AttributeRouting.Web.Http.WebHost.Framework.Factories
{
internal class AttributeRouteFactory : IAttributeRouteFactory
{
- private readonly HttpWebAttributeRoutingConfiguration _configuration;
+ private readonly HttpWebConfiguration _configuration;
- public AttributeRouteFactory(HttpWebAttributeRoutingConfiguration configuration)
+ public AttributeRouteFactory(HttpWebConfiguration configuration)
{
_configuration = configuration;
}
diff --git a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
index a004ddd..cef4ca0 100644
--- a/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/Framework/Factories/RouteConstraintFactory.cs
@@ -13,9 +13,9 @@ namespace AttributeRouting.Web.Http.WebHost.Framework.Factories
{
public class RouteConstraintFactory : IRouteConstraintFactory
{
- private readonly HttpWebAttributeRoutingConfiguration _configuration;
+ private readonly HttpWebConfiguration _configuration;
- public RouteConstraintFactory(HttpWebAttributeRoutingConfiguration configuration)
+ public RouteConstraintFactory(HttpWebConfiguration configuration)
{
_configuration = configuration;
}
diff --git a/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs b/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
index b219eb4..d8d2abb 100644
--- a/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
@@ -19,7 +19,7 @@ public static class HttpRouteCollectionExtensions
///
public static void MapHttpAttributeRoutes(this HttpRouteCollection routes)
{
- var configuration = new HttpWebAttributeRoutingConfiguration();
+ var configuration = new HttpWebConfiguration();
configuration.AddRoutesFromAssembly(Assembly.GetCallingAssembly());
routes.MapHttpAttributeRoutesInternal(configuration);
@@ -31,9 +31,9 @@ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes)
///
///
/// The initialization action that builds the configuration object
- public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, Action configurationAction)
+ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, Action configurationAction)
{
- var configuration = new HttpWebAttributeRoutingConfiguration();
+ var configuration = new HttpWebConfiguration();
configurationAction.Invoke(configuration);
routes.MapHttpAttributeRoutesInternal(configuration);
@@ -45,12 +45,12 @@ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, Actio
///
///
/// The configuration object
- public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, HttpWebAttributeRoutingConfiguration configuration)
+ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, HttpWebConfiguration configuration)
{
routes.MapHttpAttributeRoutesInternal(configuration);
}
- private static void MapHttpAttributeRoutesInternal(this HttpRouteCollection routes, HttpWebAttributeRoutingConfiguration configuration)
+ private static void MapHttpAttributeRoutesInternal(this HttpRouteCollection routes, HttpWebConfiguration configuration)
{
var generatedRoutes = new RouteBuilder(configuration).BuildAllRoutes().Cast().ToList();
diff --git a/src/AttributeRouting.Web.Http.WebHost/HttpWebAttributeRoutingConfiguration.cs b/src/AttributeRouting.Web.Http.WebHost/HttpWebConfiguration.cs
similarity index 89%
rename from src/AttributeRouting.Web.Http.WebHost/HttpWebAttributeRoutingConfiguration.cs
rename to src/AttributeRouting.Web.Http.WebHost/HttpWebConfiguration.cs
index b9e767c..3a18d25 100644
--- a/src/AttributeRouting.Web.Http.WebHost/HttpWebAttributeRoutingConfiguration.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/HttpWebConfiguration.cs
@@ -1,42 +1,42 @@
-using System;
-using System.Web.Routing;
-using AttributeRouting.Web.Http.WebHost.Framework.Factories;
-
-namespace AttributeRouting.Web.Http.WebHost
-{
- public class HttpWebAttributeRoutingConfiguration : HttpAttributeRoutingConfigurationBase
- {
- public HttpWebAttributeRoutingConfiguration()
- {
- AttributeRouteFactory = new AttributeRouteFactory(this);
- ParameterFactory = new RouteParameterFactory();
- RouteConstraintFactory = new RouteConstraintFactory(this);
-
- RouteHandlerFactory = () => null;
- RegisterDefaultInlineRouteConstraints(typeof(Web.Constraints.RegexRouteConstraint).Assembly);
- }
-
- public Func RouteHandlerFactory { get; set; }
-
- ///
- /// Specifies a function that returns an alternate route handler.
- /// By default, the route handler is the default HttpControllerRouteHandler.
- ///
- /// The route handler to use.
- public void UseRouteHandler(Func routeHandlerFactory)
- {
- RouteHandlerFactory = routeHandlerFactory;
- }
-
- ///
- /// Automatically applies the specified constaint against url parameters
- /// with names that match the given regular expression.
- ///
- /// The regex used to match url parameter names
- /// The constraint to apply to matched parameters
- public void AddDefaultRouteConstraint(string keyRegex, IRouteConstraint constraint)
- {
- base.AddDefaultRouteConstraint(keyRegex, constraint);
- }
- }
-}
+using System;
+using System.Web.Routing;
+using AttributeRouting.Web.Http.WebHost.Framework.Factories;
+
+namespace AttributeRouting.Web.Http.WebHost
+{
+ public class HttpWebConfiguration : HttpConfigurationBase
+ {
+ public HttpWebConfiguration()
+ {
+ AttributeRouteFactory = new AttributeRouteFactory(this);
+ ParameterFactory = new RouteParameterFactory();
+ RouteConstraintFactory = new RouteConstraintFactory(this);
+
+ RouteHandlerFactory = () => null;
+ RegisterDefaultInlineRouteConstraints(typeof(Web.Constraints.RegexRouteConstraint).Assembly);
+ }
+
+ public Func RouteHandlerFactory { get; set; }
+
+ ///
+ /// Specifies a function that returns an alternate route handler.
+ /// By default, the route handler is the default HttpControllerRouteHandler.
+ ///
+ /// The route handler to use.
+ public void UseRouteHandler(Func routeHandlerFactory)
+ {
+ RouteHandlerFactory = routeHandlerFactory;
+ }
+
+ ///
+ /// Automatically applies the specified constaint against url parameters
+ /// with names that match the given regular expression.
+ ///
+ /// The regex used to match url parameter names
+ /// The constraint to apply to matched parameters
+ public void AddDefaultRouteConstraint(string keyRegex, IRouteConstraint constraint)
+ {
+ base.AddDefaultRouteConstraint(keyRegex, constraint);
+ }
+ }
+}
diff --git a/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj b/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
index ec4d572..178248c 100644
--- a/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
+++ b/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
@@ -81,7 +81,7 @@
-
+
diff --git a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
index 51e5f61..f712271 100644
--- a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
+++ b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
@@ -13,7 +13,7 @@ namespace AttributeRouting.Web.Http.Framework
///
public class HttpAttributeRoute : HttpRoute, IAttributeRoute
{
- private readonly HttpAttributeRoutingConfigurationBase _configuration;
+ private readonly HttpConfigurationBase _configuration;
///
/// Route used by the AttributeRouting framework in self-host projects.
@@ -22,7 +22,7 @@ public HttpAttributeRoute(string url,
HttpRouteValueDictionary defaults,
HttpRouteValueDictionary constraints,
HttpRouteValueDictionary dataTokens,
- HttpAttributeRoutingConfigurationBase configuration)
+ HttpConfigurationBase configuration)
: base(url, defaults, constraints, dataTokens, configuration.MessageHandler)
{
_configuration = configuration;
diff --git a/src/AttributeRouting.Web.Http/HttpAttributeRoutingConfigurationBase.cs b/src/AttributeRouting.Web.Http/HttpConfigurationBase.cs
similarity index 92%
rename from src/AttributeRouting.Web.Http/HttpAttributeRoutingConfigurationBase.cs
rename to src/AttributeRouting.Web.Http/HttpConfigurationBase.cs
index c1f06bc..451132b 100644
--- a/src/AttributeRouting.Web.Http/HttpAttributeRoutingConfigurationBase.cs
+++ b/src/AttributeRouting.Web.Http/HttpConfigurationBase.cs
@@ -6,9 +6,9 @@
namespace AttributeRouting.Web.Http
{
- public abstract class HttpAttributeRoutingConfigurationBase : AttributeRoutingConfigurationBase
+ public abstract class HttpConfigurationBase : ConfigurationBase
{
- protected HttpAttributeRoutingConfigurationBase()
+ protected HttpConfigurationBase()
{
CurrentUICultureResolver = (ctx, data) => Thread.CurrentThread.CurrentUICulture.Name;
}
diff --git a/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj b/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
index a67c256..7b2f73a 100644
--- a/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
+++ b/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
@@ -53,7 +53,7 @@
SharedAssemblyInfo.cs
-
+
diff --git a/src/AttributeRouting.Web.Mvc/AttributeRoutingConfiguration.cs b/src/AttributeRouting.Web.Mvc/Configuration.cs
similarity index 93%
rename from src/AttributeRouting.Web.Mvc/AttributeRoutingConfiguration.cs
rename to src/AttributeRouting.Web.Mvc/Configuration.cs
index df5cbd1..3ac3913 100644
--- a/src/AttributeRouting.Web.Mvc/AttributeRoutingConfiguration.cs
+++ b/src/AttributeRouting.Web.Mvc/Configuration.cs
@@ -1,80 +1,80 @@
-using System;
-using System.Threading;
-using System.Web;
-using System.Web.Mvc;
-using System.Web.Routing;
-using AttributeRouting.Web.Constraints;
-using AttributeRouting.Web.Mvc.Framework.Factories;
-
-namespace AttributeRouting.Web.Mvc
-{
- public class AttributeRoutingConfiguration : AttributeRoutingConfigurationBase
- {
- public AttributeRoutingConfiguration()
- {
- AttributeRouteFactory = new AttributeRouteFactory(this);
- ParameterFactory = new RouteParameterFactory();
- RouteConstraintFactory = new RouteConstraintFactory(this);
-
- RouteHandlerFactory = () => new MvcRouteHandler();
- CurrentUICultureResolver = (ctx, data) => Thread.CurrentThread.CurrentUICulture.Name;
- RegisterDefaultInlineRouteConstraints(typeof(RegexRouteConstraint).Assembly);
- }
-
- public Func RouteHandlerFactory { get; set; }
-
- ///
- /// The controller type applicable to this context.
- ///
- public override Type FrameworkControllerType
- {
- get { return typeof(IController); }
- }
-
- ///
- /// Specifies a function that returns an alternate route handler.
- /// By default, the route handler is the default MvcRouteHandler.
- ///
- /// The route hanlder to use.
- public void UseRouteHandler(Func routeHandlerFactory)
- {
- RouteHandlerFactory = routeHandlerFactory;
- }
-
- ///
- /// Appends the routes from the specified controller type to the end of route collection.
- ///
- /// The controller type.
- public void AddRoutesFromController() where T : IController
- {
- AddRoutesFromController(typeof(T));
- }
-
- ///
- /// Appends the routes from all controllers that derive from the specified controller type to the route collection.
- ///
- /// The base controller type.
- public void AddRoutesFromControllersOfType() where T : IController
- {
- AddRoutesFromControllersOfType(typeof(T));
- }
-
- ///
- /// Automatically applies the specified constraint against url parameters
- /// with names that match the given regular expression.
- ///
- /// The regex used to match url parameter names
- /// The constraint to apply to matched parameters
- public void AddDefaultRouteConstraint(string keyRegex, IRouteConstraint constraint)
- {
- base.AddDefaultRouteConstraint(keyRegex, constraint);
- }
-
- ///
- /// This delegate returns the current UI culture name,
- /// which is used when constraining inbound routes by culture.
- /// The default delegate returns the CurrentUICulture name of the current thread.
- ///
- public Func CurrentUICultureResolver { get; set; }
- }
-}
+using System;
+using System.Threading;
+using System.Web;
+using System.Web.Mvc;
+using System.Web.Routing;
+using AttributeRouting.Web.Constraints;
+using AttributeRouting.Web.Mvc.Framework.Factories;
+
+namespace AttributeRouting.Web.Mvc
+{
+ public class Configuration : ConfigurationBase
+ {
+ public Configuration()
+ {
+ AttributeRouteFactory = new AttributeRouteFactory(this);
+ ParameterFactory = new RouteParameterFactory();
+ RouteConstraintFactory = new RouteConstraintFactory(this);
+
+ RouteHandlerFactory = () => new MvcRouteHandler();
+ CurrentUICultureResolver = (ctx, data) => Thread.CurrentThread.CurrentUICulture.Name;
+ RegisterDefaultInlineRouteConstraints(typeof(RegexRouteConstraint).Assembly);
+ }
+
+ public Func RouteHandlerFactory { get; set; }
+
+ ///
+ /// The controller type applicable to this context.
+ ///
+ public override Type FrameworkControllerType
+ {
+ get { return typeof(IController); }
+ }
+
+ ///
+ /// Specifies a function that returns an alternate route handler.
+ /// By default, the route handler is the default MvcRouteHandler.
+ ///
+ /// The route hanlder to use.
+ public void UseRouteHandler(Func routeHandlerFactory)
+ {
+ RouteHandlerFactory = routeHandlerFactory;
+ }
+
+ ///
+ /// Appends the routes from the specified controller type to the end of route collection.
+ ///
+ /// The controller type.
+ public void AddRoutesFromController() where T : IController
+ {
+ AddRoutesFromController(typeof(T));
+ }
+
+ ///
+ /// Appends the routes from all controllers that derive from the specified controller type to the route collection.
+ ///
+ /// The base controller type.
+ public void AddRoutesFromControllersOfType() where T : IController
+ {
+ AddRoutesFromControllersOfType(typeof(T));
+ }
+
+ ///
+ /// Automatically applies the specified constraint against url parameters
+ /// with names that match the given regular expression.
+ ///
+ /// The regex used to match url parameter names
+ /// The constraint to apply to matched parameters
+ public void AddDefaultRouteConstraint(string keyRegex, IRouteConstraint constraint)
+ {
+ base.AddDefaultRouteConstraint(keyRegex, constraint);
+ }
+
+ ///
+ /// This delegate returns the current UI culture name,
+ /// which is used when constraining inbound routes by culture.
+ /// The default delegate returns the CurrentUICulture name of the current thread.
+ ///
+ public Func CurrentUICultureResolver { get; set; }
+ }
+}
diff --git a/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs b/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
index 24d7d9b..1f6cf70 100644
--- a/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
+++ b/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
@@ -13,7 +13,7 @@ namespace AttributeRouting.Web.Mvc.Framework
///
public class AttributeRoute : Route, IAttributeRoute
{
- private readonly AttributeRoutingConfiguration _configuration;
+ private readonly Configuration _configuration;
///
/// Route used by the AttributeRouting framework in web projects.
@@ -22,7 +22,7 @@ public AttributeRoute(string url,
RouteValueDictionary defaults,
RouteValueDictionary constraints,
RouteValueDictionary dataTokens,
- AttributeRoutingConfiguration configuration)
+ Configuration configuration)
: base(url, defaults, constraints, dataTokens, configuration.RouteHandlerFactory())
{
_configuration = configuration;
diff --git a/src/AttributeRouting.Web.Mvc/Framework/Factories/AttributeRouteFactory.cs b/src/AttributeRouting.Web.Mvc/Framework/Factories/AttributeRouteFactory.cs
index 4d1bb43..6d8c0b9 100644
--- a/src/AttributeRouting.Web.Mvc/Framework/Factories/AttributeRouteFactory.cs
+++ b/src/AttributeRouting.Web.Mvc/Framework/Factories/AttributeRouteFactory.cs
@@ -7,9 +7,9 @@ namespace AttributeRouting.Web.Mvc.Framework.Factories
{
internal class AttributeRouteFactory : IAttributeRouteFactory
{
- private readonly AttributeRoutingConfiguration _configuration;
+ private readonly Configuration _configuration;
- public AttributeRouteFactory(AttributeRoutingConfiguration configuration)
+ public AttributeRouteFactory(Configuration configuration)
{
_configuration = configuration;
}
diff --git a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
index ac2cff7..bdfba5d 100644
--- a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs
@@ -13,9 +13,9 @@ namespace AttributeRouting.Web.Mvc.Framework.Factories
{
internal class RouteConstraintFactory : IRouteConstraintFactory
{
- private readonly AttributeRoutingConfiguration _configuration;
+ private readonly Configuration _configuration;
- public RouteConstraintFactory(AttributeRoutingConfiguration configuration)
+ public RouteConstraintFactory(Configuration configuration)
{
_configuration = configuration;
}
diff --git a/src/AttributeRouting.Web.Mvc/RouteCollectionExtensions.cs b/src/AttributeRouting.Web.Mvc/RouteCollectionExtensions.cs
index 5a511cc..12f739e 100644
--- a/src/AttributeRouting.Web.Mvc/RouteCollectionExtensions.cs
+++ b/src/AttributeRouting.Web.Mvc/RouteCollectionExtensions.cs
@@ -18,7 +18,7 @@ public static class RouteCollectionExtensions
///
public static void MapAttributeRoutes(this RouteCollection routes)
{
- var configuration = new AttributeRoutingConfiguration();
+ var configuration = new Configuration();
configuration.AddRoutesFromAssembly(Assembly.GetCallingAssembly());
routes.MapAttributeRoutesInternal(configuration);
@@ -30,9 +30,9 @@ public static void MapAttributeRoutes(this RouteCollection routes)
///
///
/// The initialization action that builds the configuration object
- public static void MapAttributeRoutes(this RouteCollection routes, Action configurationAction)
+ public static void MapAttributeRoutes(this RouteCollection routes, Action configurationAction)
{
- var configuration = new AttributeRoutingConfiguration();
+ var configuration = new Configuration();
configurationAction.Invoke(configuration);
routes.MapAttributeRoutesInternal(configuration);
@@ -44,12 +44,12 @@ public static void MapAttributeRoutes(this RouteCollection routes, Action
///
/// The configuration object
- public static void MapAttributeRoutes(this RouteCollection routes, AttributeRoutingConfiguration configuration)
+ public static void MapAttributeRoutes(this RouteCollection routes, Configuration configuration)
{
routes.MapAttributeRoutesInternal(configuration);
}
- private static void MapAttributeRoutesInternal(this RouteCollection routes, AttributeRoutingConfiguration configuration)
+ private static void MapAttributeRoutesInternal(this RouteCollection routes, Configuration configuration)
{
var generatedRoutes = new RouteBuilder(configuration).BuildAllRoutes();
diff --git a/src/AttributeRouting/AreaConfiguration.cs b/src/AttributeRouting/AreaConfiguration.cs
index e580182..b82723a 100644
--- a/src/AttributeRouting/AreaConfiguration.cs
+++ b/src/AttributeRouting/AreaConfiguration.cs
@@ -9,12 +9,12 @@ namespace AttributeRouting
public class AreaConfiguration
{
private readonly string _name;
- private readonly AttributeRoutingConfigurationBase _configuration;
+ private readonly ConfigurationBase _configuration;
///
/// Helper for configuring areas when initializing AttributeRouting framework.
///
- public AreaConfiguration(string name, AttributeRoutingConfigurationBase configuration)
+ public AreaConfiguration(string name, ConfigurationBase configuration)
{
_name = name;
_configuration = configuration;
diff --git a/src/AttributeRouting/AttributeRouting.csproj b/src/AttributeRouting/AttributeRouting.csproj
index 0deaa0c..3a6708e 100644
--- a/src/AttributeRouting/AttributeRouting.csproj
+++ b/src/AttributeRouting/AttributeRouting.csproj
@@ -58,7 +58,7 @@
SharedAssemblyInfo.cs
-
+
diff --git a/src/AttributeRouting/AttributeRoutingConfigurationBase.cs b/src/AttributeRouting/ConfigurationBase.cs
similarity index 96%
rename from src/AttributeRouting/AttributeRoutingConfigurationBase.cs
rename to src/AttributeRouting/ConfigurationBase.cs
index 9193722..1a41ca2 100644
--- a/src/AttributeRouting/AttributeRoutingConfigurationBase.cs
+++ b/src/AttributeRouting/ConfigurationBase.cs
@@ -1,282 +1,282 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text.RegularExpressions;
-using AttributeRouting.Constraints;
-using AttributeRouting.Framework;
-using AttributeRouting.Framework.Factories;
-using AttributeRouting.Framework.Localization;
-using AttributeRouting.Helpers;
-
-namespace AttributeRouting
-{
- ///
- /// Configuration options to use when generating AttributeRoutes.
- ///
- public abstract class AttributeRoutingConfigurationBase
- {
- ///
- /// Creates and initializes a new configuration object.
- ///
- protected AttributeRoutingConfigurationBase()
- {
- Assemblies = new List();
- OrderedControllerTypes = new List();
-
- InheritActionsFromBaseController = false;
-
- // Constraint setting initialization
- DefaultRouteConstraints = new Dictionary();
- InlineRouteConstraints = new Dictionary();
-
- // Translation setting initialization
- TranslationProviders = new List();
-
- // Subdomain config setting initialization
- AreaSubdomainOverrides = new Dictionary();
- DefaultSubdomain = "www";
- SubdomainParser = SubdomainParsers.ThreeSection;
-
- // AutoGenerateRouteNames config setting initialization
- RouteNameBuilder = RouteNameBuilders.FirstInWins;
- }
-
- ///
- /// Type of the framework controller (IController, IHttpController).
- ///
- public abstract Type FrameworkControllerType { get; }
-
- ///
- /// Factory for generating routes used by AttributeRouting.
- ///
- public IAttributeRouteFactory AttributeRouteFactory { get; set; }
-
- ///
- /// Factory for generating route constraints.
- ///
- public IRouteConstraintFactory RouteConstraintFactory { get; set; }
-
- ///
- /// Factory for generating optional route parameters.
- ///
- public IParameterFactory ParameterFactory { get; set; }
-
- internal List Assemblies { get; set; }
-
- internal List OrderedControllerTypes { get; set; }
-
- internal IDictionary DefaultRouteConstraints { get; set; }
-
- internal IDictionary AreaSubdomainOverrides { get; set; }
-
- ///
- /// Collection of available inline route constraint definitions.
- ///
- public IDictionary InlineRouteConstraints { get; private set; }
-
- ///
- /// Translation providers.
- ///
- public List TranslationProviders { get; set; }
-
- ///
- /// When true, the generated routes will produce lowercase URLs.
- /// The default is false.
- ///
- public bool UseLowercaseRoutes { get; set; }
-
- ///
- /// When true, the generated routes will not lowercase URL parameter values.
- /// The default is false.
- ///
- public bool PreserveCaseForUrlParameters { get; set; }
-
- ///
- /// When true, the generated routes will have a trailing slash on the path of outbound URLs.
- /// The default is false.
- ///
- public bool AppendTrailingSlash { get; set; }
-
- ///
- /// When true, the generated routes will have auto-generated route names in the form controller_action.
- /// The default is false.
- ///
- public bool AutoGenerateRouteNames { get; set; }
-
- ///
- /// Given a route specification, this delegate returns the route name
- /// to use when is true;
- ///
- public Func RouteNameBuilder { get; set; }
-
- ///
- /// Given the requested hostname, this delegate parses the subdomain.
- /// The default yields everything before the domain name;
- /// eg: www.example.com yields www, and example.com yields null.
- ///
- public Func SubdomainParser { get; set; }
-
- ///
- /// Specify the default subdomain for this application.
- /// The default is www.
- ///
- public string DefaultSubdomain { get; set; }
-
- ///
- /// When true, the generated routes will include actions defined on base controllers.
- /// The default is false.
- /// Note: Base Controllers should be declared as abstract to avoid routes being generated for them
- ///
- public bool InheritActionsFromBaseController { get; set; }
-
- ///
- /// Constrains translated routes by the thread's current UI culture.
- /// The default is false.
- ///
- public bool ConstrainTranslatedRoutesByCurrentUICulture { get; set; }
-
- ///
- /// Returns a utility for configuring areas when initializing AttributeRouting framework.
- ///
- /// The name of the area to configure
- public AreaConfiguration MapArea(string name)
- {
- return new AreaConfiguration(name, this);
- }
-
- ///
- /// Scans the assembly of the specified controller for routes to register.
- ///
- /// The type used to specify the assembly.
- [Obsolete("Prefer using AddRoutesFromController, AddRoutesFromControllersOfType, and AddRoutesFromAssembly.")]
- public void ScanAssemblyOf()
- {
- ScanAssembly(typeof(T).Assembly);
- }
-
- ///
- /// Scans the specified assembly for routes to register.
- ///
- /// The assembly.
- [Obsolete("Prefer using AddRoutesFromController, AddRoutesFromControllersOfType, and AddRoutesFromAssembly.")]
- public void ScanAssembly(Assembly assembly)
- {
- if (!Assemblies.Contains(assembly))
- Assemblies.Add(assembly);
- }
-
- ///
- /// Appends the routes from all controllers in the specified assembly to the route collection.
- ///
- /// The type denoting the assembly.
- public void AddRoutesFromAssemblyOf()
- {
- AddRoutesFromAssembly(typeof(T).Assembly);
- }
-
- ///
- /// Appends the routes from all controllers in the specified assembly to the route collection.
- ///
- /// The assembly.
- public void AddRoutesFromAssembly(Assembly assembly)
- {
- var controllerTypes = assembly.GetControllerTypes(FrameworkControllerType);
-
- foreach (var controllerType in controllerTypes)
- AddRoutesFromControllerInternal(controllerType);
- }
-
- ///
- /// Appends the routes from all controllers that derive from the specified controller type to the route collection.
- ///
- /// The base controller type.
- public void AddRoutesFromControllersOfType(Type baseControllerType)
- {
- var assembly = baseControllerType.Assembly;
-
- var controllerTypes = from controllerType in assembly.GetControllerTypes(FrameworkControllerType)
- where baseControllerType.IsAssignableFrom(controllerType)
- select controllerType;
-
- foreach (var controllerType in controllerTypes)
- AddRoutesFromControllerInternal(controllerType, true);
- }
-
- ///
- /// Appends the routes from the specified controller type to the end of route collection.
- ///
- /// The controller type.
- public void AddRoutesFromController(Type controllerType)
- {
- AddRoutesFromControllerInternal(controllerType, true);
- }
-
- ///
- /// Appends the routes from the controller to the promoted controller type list,
- /// optionally removing an already added type in order to add it to the end of the list.
- ///
- /// The controller type.
- /// Whether to remove and re-add already added controller types.
- private void AddRoutesFromControllerInternal(Type controllerType, bool reorderTypes = false)
- {
- if (!FrameworkControllerType.IsAssignableFrom(controllerType))
- return;
-
- if (!OrderedControllerTypes.Contains(controllerType))
- {
- OrderedControllerTypes.Add(controllerType);
- }
- else if (reorderTypes)
- {
- OrderedControllerTypes.Remove(controllerType);
- OrderedControllerTypes.Add(controllerType);
- }
- }
-
- protected void AddDefaultRouteConstraint(string keyRegex, object constraint)
- {
- if (!DefaultRouteConstraints.ContainsKey(keyRegex))
- DefaultRouteConstraints.Add(keyRegex, constraint);
- }
-
- ///
- /// Add a provider for translating components of routes.
- ///
- public void AddTranslationProvider()
- where TTranslationProvider : TranslationProviderBase, new()
- {
- TranslationProviders.Add(new TTranslationProvider());
- }
-
- ///
- /// Add a provider for translating components of routes.
- /// Use for a default implementation.
- ///
- public void AddTranslationProvider(TranslationProviderBase provider)
- {
- TranslationProviders.Add(provider);
- }
-
- internal IEnumerable GetTranslationProviderCultureNames()
- {
- return (from provider in TranslationProviders
- from cultureName in provider.CultureNames
- select cultureName).Distinct().ToList();
- }
-
- protected void RegisterDefaultInlineRouteConstraints(Assembly assembly)
- {
- var inlineConstraintTypes = from t in assembly.GetTypes()
- where typeof(TRouteConstraint).IsAssignableFrom(t)
- && typeof(IAttributeRouteConstraint).IsAssignableFrom(t)
- select t;
-
- foreach (var inlineConstraintType in inlineConstraintTypes)
- {
- var name = Regex.Replace(inlineConstraintType.Name, "RouteConstraint$", "").ToLowerInvariant();
- InlineRouteConstraints.Add(name, inlineConstraintType);
- }
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using AttributeRouting.Constraints;
+using AttributeRouting.Framework;
+using AttributeRouting.Framework.Factories;
+using AttributeRouting.Framework.Localization;
+using AttributeRouting.Helpers;
+
+namespace AttributeRouting
+{
+ ///
+ /// Configuration options to use when generating AttributeRoutes.
+ ///
+ public abstract class ConfigurationBase
+ {
+ ///
+ /// Creates and initializes a new configuration object.
+ ///
+ protected ConfigurationBase()
+ {
+ Assemblies = new List();
+ OrderedControllerTypes = new List();
+
+ InheritActionsFromBaseController = false;
+
+ // Constraint setting initialization
+ DefaultRouteConstraints = new Dictionary();
+ InlineRouteConstraints = new Dictionary();
+
+ // Translation setting initialization
+ TranslationProviders = new List();
+
+ // Subdomain config setting initialization
+ AreaSubdomainOverrides = new Dictionary();
+ DefaultSubdomain = "www";
+ SubdomainParser = SubdomainParsers.ThreeSection;
+
+ // AutoGenerateRouteNames config setting initialization
+ RouteNameBuilder = RouteNameBuilders.FirstInWins;
+ }
+
+ ///
+ /// Type of the framework controller (IController, IHttpController).
+ ///
+ public abstract Type FrameworkControllerType { get; }
+
+ ///
+ /// Factory for generating routes used by AttributeRouting.
+ ///
+ public IAttributeRouteFactory AttributeRouteFactory { get; set; }
+
+ ///
+ /// Factory for generating route constraints.
+ ///
+ public IRouteConstraintFactory RouteConstraintFactory { get; set; }
+
+ ///
+ /// Factory for generating optional route parameters.
+ ///
+ public IParameterFactory ParameterFactory { get; set; }
+
+ internal List Assemblies { get; set; }
+
+ internal List OrderedControllerTypes { get; set; }
+
+ internal IDictionary DefaultRouteConstraints { get; set; }
+
+ internal IDictionary AreaSubdomainOverrides { get; set; }
+
+ ///
+ /// Collection of available inline route constraint definitions.
+ ///
+ public IDictionary InlineRouteConstraints { get; private set; }
+
+ ///
+ /// Translation providers.
+ ///
+ public List TranslationProviders { get; set; }
+
+ ///
+ /// When true, the generated routes will produce lowercase URLs.
+ /// The default is false.
+ ///
+ public bool UseLowercaseRoutes { get; set; }
+
+ ///
+ /// When true, the generated routes will not lowercase URL parameter values.
+ /// The default is false.
+ ///
+ public bool PreserveCaseForUrlParameters { get; set; }
+
+ ///
+ /// When true, the generated routes will have a trailing slash on the path of outbound URLs.
+ /// The default is false.
+ ///
+ public bool AppendTrailingSlash { get; set; }
+
+ ///
+ /// When true, the generated routes will have auto-generated route names in the form controller_action.
+ /// The default is false.
+ ///
+ public bool AutoGenerateRouteNames { get; set; }
+
+ ///
+ /// Given a route specification, this delegate returns the route name
+ /// to use when is true;
+ ///
+ public Func RouteNameBuilder { get; set; }
+
+ ///
+ /// Given the requested hostname, this delegate parses the subdomain.
+ /// The default yields everything before the domain name;
+ /// eg: www.example.com yields www, and example.com yields null.
+ ///
+ public Func SubdomainParser { get; set; }
+
+ ///
+ /// Specify the default subdomain for this application.
+ /// The default is www.
+ ///
+ public string DefaultSubdomain { get; set; }
+
+ ///
+ /// When true, the generated routes will include actions defined on base controllers.
+ /// The default is false.
+ /// Note: Base Controllers should be declared as abstract to avoid routes being generated for them
+ ///
+ public bool InheritActionsFromBaseController { get; set; }
+
+ ///
+ /// Constrains translated routes by the thread's current UI culture.
+ /// The default is false.
+ ///
+ public bool ConstrainTranslatedRoutesByCurrentUICulture { get; set; }
+
+ ///
+ /// Returns a utility for configuring areas when initializing AttributeRouting framework.
+ ///
+ /// The name of the area to configure
+ public AreaConfiguration MapArea(string name)
+ {
+ return new AreaConfiguration(name, this);
+ }
+
+ ///
+ /// Scans the assembly of the specified controller for routes to register.
+ ///
+ /// The type used to specify the assembly.
+ [Obsolete("Prefer using AddRoutesFromController, AddRoutesFromControllersOfType, and AddRoutesFromAssembly.")]
+ public void ScanAssemblyOf()
+ {
+ ScanAssembly(typeof(T).Assembly);
+ }
+
+ ///
+ /// Scans the specified assembly for routes to register.
+ ///
+ /// The assembly.
+ [Obsolete("Prefer using AddRoutesFromController, AddRoutesFromControllersOfType, and AddRoutesFromAssembly.")]
+ public void ScanAssembly(Assembly assembly)
+ {
+ if (!Assemblies.Contains(assembly))
+ Assemblies.Add(assembly);
+ }
+
+ ///
+ /// Appends the routes from all controllers in the specified assembly to the route collection.
+ ///
+ /// The type denoting the assembly.
+ public void AddRoutesFromAssemblyOf()
+ {
+ AddRoutesFromAssembly(typeof(T).Assembly);
+ }
+
+ ///
+ /// Appends the routes from all controllers in the specified assembly to the route collection.
+ ///
+ /// The assembly.
+ public void AddRoutesFromAssembly(Assembly assembly)
+ {
+ var controllerTypes = assembly.GetControllerTypes(FrameworkControllerType);
+
+ foreach (var controllerType in controllerTypes)
+ AddRoutesFromControllerInternal(controllerType);
+ }
+
+ ///
+ /// Appends the routes from all controllers that derive from the specified controller type to the route collection.
+ ///
+ /// The base controller type.
+ public void AddRoutesFromControllersOfType(Type baseControllerType)
+ {
+ var assembly = baseControllerType.Assembly;
+
+ var controllerTypes = from controllerType in assembly.GetControllerTypes(FrameworkControllerType)
+ where baseControllerType.IsAssignableFrom(controllerType)
+ select controllerType;
+
+ foreach (var controllerType in controllerTypes)
+ AddRoutesFromControllerInternal(controllerType, true);
+ }
+
+ ///
+ /// Appends the routes from the specified controller type to the end of route collection.
+ ///
+ /// The controller type.
+ public void AddRoutesFromController(Type controllerType)
+ {
+ AddRoutesFromControllerInternal(controllerType, true);
+ }
+
+ ///
+ /// Appends the routes from the controller to the promoted controller type list,
+ /// optionally removing an already added type in order to add it to the end of the list.
+ ///
+ /// The controller type.
+ /// Whether to remove and re-add already added controller types.
+ private void AddRoutesFromControllerInternal(Type controllerType, bool reorderTypes = false)
+ {
+ if (!FrameworkControllerType.IsAssignableFrom(controllerType))
+ return;
+
+ if (!OrderedControllerTypes.Contains(controllerType))
+ {
+ OrderedControllerTypes.Add(controllerType);
+ }
+ else if (reorderTypes)
+ {
+ OrderedControllerTypes.Remove(controllerType);
+ OrderedControllerTypes.Add(controllerType);
+ }
+ }
+
+ protected void AddDefaultRouteConstraint(string keyRegex, object constraint)
+ {
+ if (!DefaultRouteConstraints.ContainsKey(keyRegex))
+ DefaultRouteConstraints.Add(keyRegex, constraint);
+ }
+
+ ///
+ /// Add a provider for translating components of routes.
+ ///
+ public void AddTranslationProvider()
+ where TTranslationProvider : TranslationProviderBase, new()
+ {
+ TranslationProviders.Add(new TTranslationProvider());
+ }
+
+ ///
+ /// Add a provider for translating components of routes.
+ /// Use for a default implementation.
+ ///
+ public void AddTranslationProvider(TranslationProviderBase provider)
+ {
+ TranslationProviders.Add(provider);
+ }
+
+ internal IEnumerable GetTranslationProviderCultureNames()
+ {
+ return (from provider in TranslationProviders
+ from cultureName in provider.CultureNames
+ select cultureName).Distinct().ToList();
+ }
+
+ protected void RegisterDefaultInlineRouteConstraints(Assembly assembly)
+ {
+ var inlineConstraintTypes = from t in assembly.GetTypes()
+ where typeof(TRouteConstraint).IsAssignableFrom(t)
+ && typeof(IAttributeRouteConstraint).IsAssignableFrom(t)
+ select t;
+
+ foreach (var inlineConstraintType in inlineConstraintTypes)
+ {
+ var name = Regex.Replace(inlineConstraintType.Name, "RouteConstraint$", "").ToLowerInvariant();
+ InlineRouteConstraints.Add(name, inlineConstraintType);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/AttributeRouting/Framework/AttributeRouteExtensions.cs b/src/AttributeRouting/Framework/AttributeRouteExtensions.cs
index 0eb2780..fa63f3b 100644
--- a/src/AttributeRouting/Framework/AttributeRouteExtensions.cs
+++ b/src/AttributeRouting/Framework/AttributeRouteExtensions.cs
@@ -45,7 +45,7 @@ public static bool IsLeftPartOfUrlMatched(this IAttributeRoute route, string req
/// The host from the current request
/// The configuration for the route
/// True if the subdomain for this route matches the current request host.
- public static bool IsSubdomainMatched(this IAttributeRoute route, string host, AttributeRoutingConfigurationBase configuration)
+ public static bool IsSubdomainMatched(this IAttributeRoute route, string host, ConfigurationBase configuration)
{
// If no subdomains are mapped with AR, then yes.
if (!route.MappedSubdomains.Any())
@@ -73,7 +73,7 @@ public static bool IsSubdomainMatched(this IAttributeRoute route, string host, A
///
///
///
- public static bool IsCultureNameMatched(this IAttributeRoute route, string currentUICultureName, AttributeRoutingConfigurationBase configuration)
+ public static bool IsCultureNameMatched(this IAttributeRoute route, string currentUICultureName, ConfigurationBase configuration)
{
if (!configuration.ConstrainTranslatedRoutesByCurrentUICulture)
return true;
@@ -190,7 +190,7 @@ public static TVirtualPathData GetTranslatedVirtualPath(this I
/// The current virtual path, after translation
/// The configuration for the route
/// The final virtual path
- public static string GetFinalVirtualPath(this IAttributeRoute route, string virtualPath, AttributeRoutingConfigurationBase configuration)
+ public static string GetFinalVirtualPath(this IAttributeRoute route, string virtualPath, ConfigurationBase configuration)
{
/**
* Lowercase urls.
diff --git a/src/AttributeRouting/Framework/IAttributeRoute.cs b/src/AttributeRouting/Framework/IAttributeRoute.cs
index 02360f2..61b469e 100644
--- a/src/AttributeRouting/Framework/IAttributeRoute.cs
+++ b/src/AttributeRouting/Framework/IAttributeRoute.cs
@@ -40,19 +40,19 @@ public interface IAttributeRoute
string Url { get; set; }
///
- /// If true, will override
+ /// If true, will override
/// set via global configuration and the generated route will have a lowercase URL.
///
bool? UseLowercaseRoute { get; set; }
///
- /// If true, will override
+ /// If true, will override
/// set via global configuration and the generated route will not lowercase URL parameter values.
///
bool? PreserveCaseForUrlParameters { get; set; }
///
- /// If true, will override
+ /// If true, will override
/// set via global configuration and the generated route will have a trailing slash on the path of outbound URLs.
///
bool? AppendTrailingSlash { get; set; }
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index d8f62ca..793277b 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -10,16 +10,16 @@ namespace AttributeRouting.Framework
{
///
/// Creates objects according to the
- /// options set in implementations of .
+ /// options set in implementations of .
///
public class RouteBuilder
{
- private readonly AttributeRoutingConfigurationBase _configuration;
+ private readonly ConfigurationBase _configuration;
private readonly IAttributeRouteFactory _routeFactory;
private readonly IRouteConstraintFactory _routeConstraintFactory;
private readonly IParameterFactory _parameterFactory;
- public RouteBuilder(AttributeRoutingConfigurationBase configuration)
+ public RouteBuilder(ConfigurationBase configuration)
{
if (configuration == null) throw new ArgumentNullException("configuration");
diff --git a/src/AttributeRouting/Framework/RouteReflector.cs b/src/AttributeRouting/Framework/RouteReflector.cs
index b7168ea..68b18b9 100644
--- a/src/AttributeRouting/Framework/RouteReflector.cs
+++ b/src/AttributeRouting/Framework/RouteReflector.cs
@@ -8,13 +8,13 @@ namespace AttributeRouting.Framework
{
///
/// Creates objects according to the
- /// options set in implementations of .
+ /// options set in implementations of .
///
public class RouteReflector
{
- private readonly AttributeRoutingConfigurationBase _configuration;
+ private readonly ConfigurationBase _configuration;
- public RouteReflector(AttributeRoutingConfigurationBase configuration)
+ public RouteReflector(ConfigurationBase configuration)
{
if (configuration == null) throw new ArgumentNullException("configuration");
diff --git a/src/AttributeRouting/IRouteAttribute.cs b/src/AttributeRouting/IRouteAttribute.cs
index d35de64..7595a0e 100644
--- a/src/AttributeRouting/IRouteAttribute.cs
+++ b/src/AttributeRouting/IRouteAttribute.cs
@@ -78,7 +78,7 @@ public interface IRouteAttribute
string TranslationKey { get; set; }
///
- /// If set, will override
+ /// If set, will override
/// set via global configuration for this route.
///
bool UseLowercaseRoute { get; set; }
@@ -89,7 +89,7 @@ public interface IRouteAttribute
bool? UseLowercaseRouteFlag { get; }
///
- /// If set, will override
+ /// If set, will override
/// set via global configuration for this route.
///
bool PreserveCaseForUrlParameters { get; set; }
@@ -100,7 +100,7 @@ public interface IRouteAttribute
bool? PreserveCaseForUrlParametersFlag { get; }
///
- /// If true, will override
+ /// If true, will override
/// set via global configuration for this route.
///
bool AppendTrailingSlash { get; set; }
From 3cd051f9b452d6c0a33b995a1c438862d90baa51 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Fri, 21 Dec 2012 17:15:55 -0700
Subject: [PATCH 25/97] #175 - fixed logic for finding default area name.
---
.../AttributeRouting.Specs.csproj | 1 +
.../Features/RouteAreas.feature | 4 ++--
.../Features/RouteAreas.feature.cs | 4 ++--
.../Areas/Sample/DefaultRouteAreaController.cs | 15 +++++++++++++++
.../Subjects/RouteAreasControllers.cs | 10 ----------
src/AttributeRouting/Framework/RouteReflector.cs | 4 ++--
.../Helpers/ReflectionExtensions.cs | 14 +++++++++++---
7 files changed, 33 insertions(+), 19 deletions(-)
create mode 100644 src/AttributeRouting.Specs/Subjects/Areas/Sample/DefaultRouteAreaController.cs
diff --git a/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj b/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
index e502b37..c2cb4a3 100644
--- a/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
+++ b/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj
@@ -141,6 +141,7 @@
+
diff --git a/src/AttributeRouting.Specs/Features/RouteAreas.feature b/src/AttributeRouting.Specs/Features/RouteAreas.feature
index ace3daa..dea75a6 100644
--- a/src/AttributeRouting.Specs/Features/RouteAreas.feature
+++ b/src/AttributeRouting.Specs/Features/RouteAreas.feature
@@ -41,6 +41,6 @@ Scenario: Generating area routes with an explicit area url when route urls speci
Scenario: Generating area routes with the default ctor of the RouteAreaAttribute
Given I have registered the routes for the DefaultRouteAreaController
When I fetch the routes for the DefaultRouteArea controller's Index action
- Then the route url is "Subjects/Index"
- And the data token for "area" is "Subjects"
+ Then the route url is "Sample/Index"
+ And the data token for "area" is "Sample"
diff --git a/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs b/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs
index 51449c0..d6f9ed5 100644
--- a/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteAreas.feature.cs
@@ -203,9 +203,9 @@ public virtual void GeneratingAreaRoutesWithTheDefaultCtorOfTheRouteAreaAttribut
#line 43
testRunner.When("I fetch the routes for the DefaultRouteArea controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 44
- testRunner.Then("the route url is \"Subjects/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+ testRunner.Then("the route url is \"Sample/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 45
- testRunner.And("the data token for \"area\" is \"Subjects\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+ testRunner.And("the data token for \"area\" is \"Sample\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Subjects/Areas/Sample/DefaultRouteAreaController.cs b/src/AttributeRouting.Specs/Subjects/Areas/Sample/DefaultRouteAreaController.cs
new file mode 100644
index 0000000..8c657b8
--- /dev/null
+++ b/src/AttributeRouting.Specs/Subjects/Areas/Sample/DefaultRouteAreaController.cs
@@ -0,0 +1,15 @@
+using System.Web.Mvc;
+using AttributeRouting.Web.Mvc;
+
+namespace AttributeRouting.Specs.Subjects.Areas.Sample
+{
+ [RouteArea]
+ public class DefaultRouteAreaController : Controller
+ {
+ [GET("Index")]
+ public ActionResult Index()
+ {
+ return Content("");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs b/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs
index e9228cf..4dcf68b 100644
--- a/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/RouteAreasControllers.cs
@@ -52,14 +52,4 @@ public ActionResult DuplicatePrefix()
return Content("");
}
}
-
- [RouteArea]
- public class DefaultRouteAreaController : Controller
- {
- [GET("Index")]
- public ActionResult Index()
- {
- return Content("");
- }
- }
}
diff --git a/src/AttributeRouting/Framework/RouteReflector.cs b/src/AttributeRouting/Framework/RouteReflector.cs
index 68b18b9..a0a86b9 100644
--- a/src/AttributeRouting/Framework/RouteReflector.cs
+++ b/src/AttributeRouting/Framework/RouteReflector.cs
@@ -207,7 +207,7 @@ private static string GetAreaName(RouteAreaAttribute routeAreaAttribute, Type co
// If given an area name, then use it.
// Otherwise, use the last section of the namespace of the controller, as a convention.
- return routeAreaAttribute.AreaName ?? controllerType.GetLastSectionOfNamespace();
+ return routeAreaAttribute.AreaName ?? controllerType.GetConventionalAreaName();
}
///
@@ -232,7 +232,7 @@ private static string GetAreaUrl(RouteAreaAttribute routeAreaAttribute, string s
// If we're given an area url or an area name, then use it.
// Otherwise, use the last section of the namespace of the controller, as a convention.
var areaUrlOrName = routeAreaAttribute.AreaUrl ?? routeAreaAttribute.AreaName;
- return areaUrlOrName ?? controllerType.GetLastSectionOfNamespace();
+ return areaUrlOrName ?? controllerType.GetConventionalAreaName();
}
///
diff --git a/src/AttributeRouting/Helpers/ReflectionExtensions.cs b/src/AttributeRouting/Helpers/ReflectionExtensions.cs
index c04e77f..51f8239 100644
--- a/src/AttributeRouting/Helpers/ReflectionExtensions.cs
+++ b/src/AttributeRouting/Helpers/ReflectionExtensions.cs
@@ -8,10 +8,18 @@ namespace AttributeRouting.Helpers
{
public static class ReflectionExtensions
{
- public static string GetLastSectionOfNamespace(this Type type)
+ public static string GetConventionalAreaName(this Type type)
{
- var ns = type.Namespace;
- return ns == null ? null : ns.Split('.').Last();
+ var typeNameSpace = type.Namespace;
+ if (typeNameSpace == null)
+ return null;
+
+ return typeNameSpace
+ .Split('.')
+ .SkipWhile(s => !s.ValueEquals("Areas"))
+ .Skip(1) // move past "Areas"
+ .Take(1) // take the next
+ .FirstOrDefault();
}
public static IEnumerable GetActionMethods(this Type type, bool inheritActionsFromBaseController)
From 2737bfe1a183a0e809ddc44b1a34bafbf7387661 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Fri, 21 Dec 2012 17:19:01 -0700
Subject: [PATCH 26/97] updated readme and assembly info for 3.4.1
---
README.textile | 4 ++++
src/SharedAssemblyInfo.cs | 6 +++---
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/README.textile b/README.textile
index 3ec8bd1..ebd17cb 100644
--- a/README.textile
+++ b/README.textile
@@ -4,6 +4,10 @@ h2. Please refer to "attributerouting.net":http://attributerouting.net/ for docu
h3. Changelog
+_3.4.1_
+
+* #175 - fixed bug in getting the default area name for a controller.
+
_3.4_
* #124 - Now supporting custom IRouteHandler in web-host scenario. Also supporting custom HttpMessageHandler for Web API.
diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs
index 2a41a79..29fcad0 100644
--- a/src/SharedAssemblyInfo.cs
+++ b/src/SharedAssemblyInfo.cs
@@ -7,7 +7,7 @@
[assembly: AssemblyProduct("AttributeRouting")]
[assembly: AssemblyCopyright("Copyright 2010-2012 Tim McCall")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("3.4")]
-[assembly: AssemblyFileVersion("3.4")]
-[assembly: AssemblyInformationalVersion("3.4")]
+[assembly: AssemblyVersion("3.4.1")]
+[assembly: AssemblyFileVersion("3.4.1")]
+[assembly: AssemblyInformationalVersion("3.4.1")]
[assembly: AssemblyConfiguration("Release")]
From cecfd91a1cc97a61e0bd1958884280de8c353dfd Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Fri, 21 Dec 2012 18:02:08 -0700
Subject: [PATCH 27/97] #173 - strong signing assemblies.
---
.../AttributeRouting.Web.Http.SelfHost.csproj | 7 +++++++
.../AttributeRouting.snk | Bin 0 -> 596 bytes
.../AttributeRouting.Web.Http.WebHost.csproj | 7 +++++++
.../AttributeRouting.snk | Bin 0 -> 596 bytes
.../AttributeRouting.Web.Http.csproj | 7 +++++++
.../AttributeRouting.snk | Bin 0 -> 596 bytes
.../AttributeRouting.Web.Mvc.csproj | 9 +++++++++
src/AttributeRouting.Web.Mvc/AttributeRouting.snk | Bin 0 -> 596 bytes
.../AttributeRouting.Web.csproj | 10 +++++++++-
src/AttributeRouting.Web/AttributeRouting.snk | Bin 0 -> 596 bytes
src/AttributeRouting.snk | Bin 0 -> 596 bytes
src/AttributeRouting/AttributeRouting.csproj | 9 +++++++++
src/AttributeRouting/AttributeRouting.snk | Bin 0 -> 596 bytes
13 files changed, 48 insertions(+), 1 deletion(-)
create mode 100644 src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.snk
create mode 100644 src/AttributeRouting.Web.Http.WebHost/AttributeRouting.snk
create mode 100644 src/AttributeRouting.Web.Http/AttributeRouting.snk
create mode 100644 src/AttributeRouting.Web.Mvc/AttributeRouting.snk
create mode 100644 src/AttributeRouting.Web/AttributeRouting.snk
create mode 100644 src/AttributeRouting.snk
create mode 100644 src/AttributeRouting/AttributeRouting.snk
diff --git a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
index f404f8a..2d38ede 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
+++ b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
@@ -36,6 +36,12 @@
bin\Release\AttributeRouting.Web.Http.SelfHost.xml
1591, 1587
+
+ true
+
+
+ AttributeRouting.snk
+
..\packages\Newtonsoft.Json.4.5.8\lib\net40\Newtonsoft.Json.dll
@@ -88,6 +94,7 @@
+
diff --git a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.snk b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.snk
new file mode 100644
index 0000000000000000000000000000000000000000..b5ab91c2a292360331d78f208218a036f4b58158
GIT binary patch
literal 596
zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097#FKi0%iVOb>nhNTb6oAn@5Wl-Ypa0MC
z&rDP{JXmd`5);UKJA^@|66-Ob?WIsSQ%#9b_o2Xg_1BpO#uo$&t=(0dj{P*0R}Asp
zDyc?k!X3Zri**9ilE&i2G1i5b$vfMzrf>TMu~|v{-;DM~s=tr{lV9kV6$CIMtBsx|
zvT9T8aIH*7HI6l>;HvEqXg*VIF}ri6a7A%m(MyOju+e|(p=QA|rx@h5F@d2aoHzYk
zm1pVD2cU~M3@gf4fk8^GJTf|O_D>dq(oXGTp(eiqu8^`f%;Jq*YsdXS060F+_Gc#;
zcswtNqp0vUEo*mjq1b}FZftx2mc_^kA$<6G1dSaJ7F9u)ZgNC}CXyijzMbY<_cZhq
zDz7bMV6(Pz=8%8ubhA~~k(k~8*E;*}q68KuCQg&PF1>6yy~^$~&i@ixmC4H<1Nl=4
zEhfs5>uO0fvvm}*qFm8g6-1e8g7nUFlAiJYzv7+@`JbnSvL8w?TDY9a5jnr^)~GI)
zL(y3@I<9E6+Mk)-1PNsO>jn)=_*+==V*|6_JD)ZBriW9!WuUDP1pp&7AaWctr3y^h@{
zHJzhy*T5Lb*2a=F?!^C#UmEeSW4nPW2^0d?#1>TqKnrf=&-a*
iqRt~2`(LB;RF$;EnkSN)tQ=K9mJWy~@EFoK^wd(n3Mm%=
literal 0
HcmV?d00001
diff --git a/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj b/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj
index 171927e..4251784 100644
--- a/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj
+++ b/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj
@@ -36,6 +36,12 @@
bin\Release\AttributeRouting.Web.Http.WebHost.xml
1591, 1587
+
+ true
+
+
+ AttributeRouting.snk
+
True
@@ -96,6 +102,7 @@
+
diff --git a/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.snk b/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.snk
new file mode 100644
index 0000000000000000000000000000000000000000..b5ab91c2a292360331d78f208218a036f4b58158
GIT binary patch
literal 596
zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097#FKi0%iVOb>nhNTb6oAn@5Wl-Ypa0MC
z&rDP{JXmd`5);UKJA^@|66-Ob?WIsSQ%#9b_o2Xg_1BpO#uo$&t=(0dj{P*0R}Asp
zDyc?k!X3Zri**9ilE&i2G1i5b$vfMzrf>TMu~|v{-;DM~s=tr{lV9kV6$CIMtBsx|
zvT9T8aIH*7HI6l>;HvEqXg*VIF}ri6a7A%m(MyOju+e|(p=QA|rx@h5F@d2aoHzYk
zm1pVD2cU~M3@gf4fk8^GJTf|O_D>dq(oXGTp(eiqu8^`f%;Jq*YsdXS060F+_Gc#;
zcswtNqp0vUEo*mjq1b}FZftx2mc_^kA$<6G1dSaJ7F9u)ZgNC}CXyijzMbY<_cZhq
zDz7bMV6(Pz=8%8ubhA~~k(k~8*E;*}q68KuCQg&PF1>6yy~^$~&i@ixmC4H<1Nl=4
zEhfs5>uO0fvvm}*qFm8g6-1e8g7nUFlAiJYzv7+@`JbnSvL8w?TDY9a5jnr^)~GI)
zL(y3@I<9E6+Mk)-1PNsO>jn)=_*+==V*|6_JD)ZBriW9!WuUDP1pp&7AaWctr3y^h@{
zHJzhy*T5Lb*2a=F?!^C#UmEeSW4nPW2^0d?#1>TqKnrf=&-a*
iqRt~2`(LB;RF$;EnkSN)tQ=K9mJWy~@EFoK^wd(n3Mm%=
literal 0
HcmV?d00001
diff --git a/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj b/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
index 178248c..7a09a9f 100644
--- a/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
+++ b/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
@@ -36,6 +36,12 @@
bin\Release\AttributeRouting.Web.Http.xml
1591, 1587
+
+ true
+
+
+ AttributeRouting.snk
+
..\packages\Newtonsoft.Json.4.5.8\lib\net40\Newtonsoft.Json.dll
@@ -94,6 +100,7 @@
+
diff --git a/src/AttributeRouting.Web.Http/AttributeRouting.snk b/src/AttributeRouting.Web.Http/AttributeRouting.snk
new file mode 100644
index 0000000000000000000000000000000000000000..b5ab91c2a292360331d78f208218a036f4b58158
GIT binary patch
literal 596
zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097#FKi0%iVOb>nhNTb6oAn@5Wl-Ypa0MC
z&rDP{JXmd`5);UKJA^@|66-Ob?WIsSQ%#9b_o2Xg_1BpO#uo$&t=(0dj{P*0R}Asp
zDyc?k!X3Zri**9ilE&i2G1i5b$vfMzrf>TMu~|v{-;DM~s=tr{lV9kV6$CIMtBsx|
zvT9T8aIH*7HI6l>;HvEqXg*VIF}ri6a7A%m(MyOju+e|(p=QA|rx@h5F@d2aoHzYk
zm1pVD2cU~M3@gf4fk8^GJTf|O_D>dq(oXGTp(eiqu8^`f%;Jq*YsdXS060F+_Gc#;
zcswtNqp0vUEo*mjq1b}FZftx2mc_^kA$<6G1dSaJ7F9u)ZgNC}CXyijzMbY<_cZhq
zDz7bMV6(Pz=8%8ubhA~~k(k~8*E;*}q68KuCQg&PF1>6yy~^$~&i@ixmC4H<1Nl=4
zEhfs5>uO0fvvm}*qFm8g6-1e8g7nUFlAiJYzv7+@`JbnSvL8w?TDY9a5jnr^)~GI)
zL(y3@I<9E6+Mk)-1PNsO>jn)=_*+==V*|6_JD)ZBriW9!WuUDP1pp&7AaWctr3y^h@{
zHJzhy*T5Lb*2a=F?!^C#UmEeSW4nPW2^0d?#1>TqKnrf=&-a*
iqRt~2`(LB;RF$;EnkSN)tQ=K9mJWy~@EFoK^wd(n3Mm%=
literal 0
HcmV?d00001
diff --git a/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj b/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
index 7b2f73a..5554645 100644
--- a/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
+++ b/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
@@ -36,6 +36,12 @@
bin\Release\AttributeRouting.Web.Mvc.xml
1591, 1587
+
+ true
+
+
+ AttributeRouting.snk
+
@@ -79,6 +85,9 @@
AttributeRouting
+
+
+
+ -->
\ No newline at end of file
diff --git a/src/AttributeRouting.Specs/Tests/Extensions/UrlHelperExtensionTests.cs b/src/AttributeRouting.Specs/Tests/Extensions/UrlHelperExtensionTests.cs
new file mode 100644
index 0000000..dfb9810
--- /dev/null
+++ b/src/AttributeRouting.Specs/Tests/Extensions/UrlHelperExtensionTests.cs
@@ -0,0 +1,242 @@
+using System;
+using System.Collections.Specialized;
+using System.Web;
+using System.Web.Mvc;
+using System.Web.Routing;
+using AttributeRouting.Specs.Subjects;
+using AttributeRouting.Web.Mvc;
+using AttributeRouting.Web.Mvc.Extensions;
+using Moq;
+using NUnit.Framework;
+
+namespace AttributeRouting.Specs.Tests.Extensions
+{
+ public class UrlHelperExtensionTests
+ {
+ private UrlHelper GetUrlHelper(RouteCollection routes, string host, string schema = "http")
+ {
+ Mock httpContextMock = MockBuilder.BuildMockHttpContext(r =>
+ {
+ r.SetupGet(x => x.Url).Returns(new Uri(schema + "://" + host, UriKind.Absolute));
+ r.SetupGet(x => x.Headers).Returns(new NameValueCollection { { "host", host } });
+ });
+
+ return new UrlHelper(new RequestContext(httpContextMock.Object, new RouteData()), routes);
+ }
+
+ [Test]
+ public void Local_Host_Action_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config => config.AddRoutesFromController());
+ var helper = GetUrlHelper(routes, "localhost");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("Index", "Subdomain", "Users");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.localhost/", path);
+ }
+
+
+ [Test]
+ public void Top_Level_Domain_Action_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config => config.AddRoutesFromController());
+ var helper = GetUrlHelper(routes, "example.com");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("Index", "Subdomain", "Users");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.example.com/", path);
+ }
+
+ [Test]
+ public void Second_Level_Domain_Action_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config => config.AddRoutesFromController());
+ var helper = GetUrlHelper(routes, "example.co.uk");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("Index", "Subdomain", "Users");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.example.co.uk/", path);
+ }
+
+ [Test]
+ public void Top_Level_Domain_With_Subdomain_Action_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ });
+ var helper = GetUrlHelper(routes, "private.example.com");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("Index", "Subdomain", "Users");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.example.com/", path);
+ }
+
+ [Test]
+ public void Second_Level_Domain_With_Subdomain_Action_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ });
+ var helper = GetUrlHelper(routes, "private.example.co.ok");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("Index", "Subdomain", "Users");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.example.co.ok/", path);
+ }
+
+ [Test]
+ public void Current_Subdomain_Action_To_Same_Subdomain_Will_Only_Be_The_Relative_Url()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config => config.AddRoutesFromController());
+ var helper = GetUrlHelper(routes, "users.example.com");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("Index", "Subdomain", "Users");
+ Console.WriteLine(path);
+ Assert.AreEqual("/", path);
+ }
+
+ [Test]
+ public void Current_Subdomain_Action_To_No_Subdomain_And_Default_Subdomain_Will_Return_Full_Url()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ });
+ var helper = GetUrlHelper(routes, "users.example.com");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("AnyVerb", "StandardUsage");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://www.example.com/AnyVerb", path);
+ }
+
+ [Test]
+ public void Current_Subdomain_Action_To_No_Subdomain_And_Custom_Default_Subdomain_Will_Return_Full_Url()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ config.DefaultSubdomain = "xyz";
+ });
+ var helper = GetUrlHelper(routes, "users.example.com");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("AnyVerb", "StandardUsage");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://xyz.example.com/AnyVerb", path);
+ }
+
+ [Test]
+ public void Current_Subdomain_Action_To_No_Subdomain_And_No_Default_Subdomain_Will_Return_Full_Url()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ config.DefaultSubdomain = "";
+ });
+ var helper = GetUrlHelper(routes, "users.example.com");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("AnyVerb", "StandardUsage");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://example.com/AnyVerb", path);
+ }
+
+ [Test]
+ public void Local_Host_With_Subdomain_Action_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ });
+ var helper = GetUrlHelper(routes, "private.localhost");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("Index", "Subdomain", "Users");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.localhost/", path);
+ }
+
+
+ [Test]
+ public void Local_Host_With_Subdomain_Action_To_Non_Area_Method_Will_Return_Full_Url_Without_Default_Subdomain()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ });
+ var helper = GetUrlHelper(routes, "users.localhost");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("Index", "StandardUsage");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://localhost/", path);
+ }
+
+
+ [Test]
+ public void HTTP_URL_Will_Change_To_HTTPS()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config => config.AddRoutesFromController());
+ var helper = GetUrlHelper(routes, "localhost");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("Index", "Subdomain", new {area = "Users"}, "https");
+ Console.WriteLine(path);
+ Assert.AreEqual("https://users.localhost/", path);
+ }
+
+ [Test]
+ public void HTTPS_URL_Will_Change_To_HTTP()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config => config.AddRoutesFromController());
+ var helper = GetUrlHelper(routes, "localhost", "https");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("Index", "Subdomain", new { area = "Users" }, "http");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.localhost/", path);
+ }
+
+ [Test]
+ public void Host_With_Port_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config => config.AddRoutesFromController());
+ var helper = GetUrlHelper(routes, "example.com:81");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainAction("Index", "Subdomain", new { area = "Users" });
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.example.com:81/", path);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests.Web/Views/Web.config b/src/AttributeRouting.Tests.Web/Views/Web.config
index 4c30ef2..b38c926 100644
--- a/src/AttributeRouting.Tests.Web/Views/Web.config
+++ b/src/AttributeRouting.Tests.Web/Views/Web.config
@@ -1,58 +1,60 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj b/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
index 5554645..d598b27 100644
--- a/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
+++ b/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
@@ -1,100 +1,101 @@
-
-
-
- Debug
- AnyCPU
- 8.0.30703
- 2.0
- {4604C450-EBF8-4A7F-BD3A-A24655C41FA4}
- Library
- Properties
- AttributeRouting.Web.Mvc
- AttributeRouting.Web.Mvc
- v4.0
- 512
- ..\
- true
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
- bin\Release\AttributeRouting.Web.Mvc.xml
- 1591, 1587
-
-
- true
-
-
- AttributeRouting.snk
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- SharedAssemblyInfo.cs
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {C91C065B-A821-4890-9F31-F9E245D804D1}
- AttributeRouting.Web
-
-
- {871A79CF-C705-4C6B-8938-F9AA1E02AEA4}
- AttributeRouting
-
-
-
-
-
-
-
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {4604C450-EBF8-4A7F-BD3A-A24655C41FA4}
+ Library
+ Properties
+ AttributeRouting.Web.Mvc
+ AttributeRouting.Web.Mvc
+ v4.0
+ 512
+ ..\
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ bin\Release\AttributeRouting.Web.Mvc.xml
+ 1591, 1587
+
+
+ true
+
+
+ AttributeRouting.snk
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SharedAssemblyInfo.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {C91C065B-A821-4890-9F31-F9E245D804D1}
+ AttributeRouting.Web
+
+
+ {871A79CF-C705-4C6B-8938-F9AA1E02AEA4}
+ AttributeRouting
+
+
+
+
+
+
+
+ -->
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs b/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs
new file mode 100644
index 0000000..8f233ac
--- /dev/null
+++ b/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs
@@ -0,0 +1,276 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Web.Mvc;
+using System.Web.Routing;
+using AttributeRouting.Framework;
+using AttributeRouting.Helpers;
+
+namespace AttributeRouting.Web.Mvc.Extensions
+{
+ public static class UrlHelperExtensions
+ {
+ public static string SubdomainAction(this UrlHelper urlHelper, string actionName,
+ RouteValueDictionary routeValues)
+ {
+ string baseUrl = GetDomainBase(urlHelper, null, actionName, null, routeValues);
+ return BuildUri(baseUrl, urlHelper.Action(actionName, routeValues));
+ }
+
+ public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName,
+ string areaName = "")
+ {
+ var routeValues = new RouteValueDictionary {{"area", areaName}};
+ string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName, routeValues);
+ return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues));
+ }
+
+ public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName,
+ object routeValues)
+ {
+ string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName,
+ new RouteValueDictionary(routeValues));
+ return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues));
+ }
+
+ public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName,
+ RouteValueDictionary routeValues)
+ {
+ string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName, routeValues);
+ return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues));
+ }
+
+ public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName,
+ object routeValues, string protocol)
+ {
+ string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName,
+ new RouteValueDictionary(routeValues), protocol);
+ return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues));
+ }
+
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, object routeValues)
+ {
+ return SubdomainRouteUrl(urlHelper, null, routeValues);
+ }
+
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, RouteValueDictionary routeValues)
+ {
+ return SubdomainRouteUrl(urlHelper, null, routeValues);
+ }
+
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName)
+ {
+ return SubdomainRouteUrl(urlHelper, routeName, (object) null);
+ }
+
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName, object routeValues)
+ {
+ return SubdomainRouteUrl(urlHelper, routeName, routeValues, null);
+ }
+
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName,
+ RouteValueDictionary routeValues)
+ {
+ string baseUrl = GetDomainBase(urlHelper, routeName, null, null, routeValues);
+ return BuildUri(baseUrl, urlHelper.RouteUrl(routeName, routeValues));
+ }
+
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName, object routeValues,
+ string protocol)
+ {
+ var r = new RouteValueDictionary(routeValues);
+ string baseUrl = GetDomainBase(urlHelper, routeName, null, null, r, protocol);
+ return BuildUri(baseUrl, urlHelper.RouteUrl(routeName, routeValues));
+ }
+
+ ///
+ /// Gets the domain base url.
+ ///
+ /// The URL helper.
+ /// Name of the route.
+ /// Name of the action.
+ /// Name of the controller.
+ /// The route values.
+ /// The schema.
+ ///
+ private static string GetDomainBase(UrlHelper urlHelper, string routeName, string actionName,
+ string controllerName, RouteValueDictionary routeValues,
+ string schema = null)
+ {
+ //baseUrl is the return value which by default is an empty string
+ string baseUrl = string.Empty;
+
+ //just a shortcut variable so we don't have to have this the below line eight million times
+ Uri currentUrl = urlHelper.RequestContext.HttpContext.Request.Url;
+
+ //get the desired route using a copy of MS internal methods
+ RouteValueDictionary values = MergeRouteValues(actionName, controllerName, urlHelper.RequestContext.RouteData.Values, routeValues, true);
+ VirtualPathData virtualPathForArea = urlHelper.RouteCollection.GetVirtualPathForArea(urlHelper.RequestContext, routeName, values);
+ if (virtualPathForArea == null)
+ {
+ return baseUrl;
+ }
+
+ //if not a AttributeRoute or the current url is funny then nothing we can do so move on
+ var route = virtualPathForArea.Route as IAttributeRoute;
+ if (route != null && currentUrl != null && !string.IsNullOrWhiteSpace(currentUrl.OriginalString))
+ {
+ //get the current domain via the current Uri.
+ string host = currentUrl.GetLeftPart(UriPartial.Authority).Replace(currentUrl.GetLeftPart(UriPartial.Scheme), string.Empty);
+
+ IPAddress ip;
+ //if the port exists in the host remove it so that we don't run into trouble with the IPAddress parsing
+ if (host.Contains(":"))
+ {
+ host = host.Substring(0, host.IndexOf(":", StringComparison.Ordinal));
+ }
+
+ //if an ip then no point in building a subdomain for it
+ if (IPAddress.TryParse(host, out ip))
+ {
+ return string.Empty;
+ }
+
+ //save the current host for comparisons later
+ string currentHost = host;
+
+ //which protocol schema to use. i.e. http, https
+ string scheme = schema ?? currentUrl.Scheme;
+
+ //what is the current port. needed if non-standard
+ int port = currentUrl.Port;
+
+ //is the port a standard port?
+ bool useDefaultPort = port == 80 || port == 443;
+
+ //need the default subdomain incase we are going from one subdomain method to a non-subdomain method
+ string defaultSubdomain = string.Empty;
+ if (route.DataTokens.Any(x => x.Key.Equals("defaultSubdomain")))
+ {
+ defaultSubdomain = route.DataTokens["defaultSubdomain"].ToString();
+ }
+
+ //if the host contains a dot we need to remove the subdomain if it is in the list of ones to remove
+ if (host.Contains("."))
+ {
+ //get all registered subdomains
+ List subdomains =
+ urlHelper.RouteCollection.Where(x => x is IAttributeRoute)
+ .Cast()
+ .Where(x => x.Subdomain.HasValue())
+ .Select(x => x.Subdomain)
+ .Distinct()
+ .ToList();
+
+ //also add the default subdomain from the current route
+ if (!string.IsNullOrWhiteSpace(defaultSubdomain) && !subdomains.Contains(defaultSubdomain))
+ {
+ subdomains.Add(defaultSubdomain);
+ }
+
+ //strips subdomain information off of current (if matching a current one)
+ string subDomainSection = host.Split('.')[0];
+ foreach (
+ string subdomain in
+ subdomains.Where(
+ subdomain =>
+ subDomainSection.Equals(subdomain, StringComparison.InvariantCultureIgnoreCase)))
+ {
+ host = host.Replace(string.Format("{0}.", subdomain), string.Empty);
+ break;
+ }
+ }
+
+ //if not a subdomain then don't build the url. instead build it to the default subdomain
+ if (!string.IsNullOrWhiteSpace(route.Subdomain))
+ {
+ //if host already starts with subdomain then skip building the url
+ if (!currentHost.StartsWith(route.Subdomain))
+ {
+ baseUrl = string.Format("{0}://{1}.{2}", scheme, route.Subdomain, host);
+ }
+ }
+ else
+ {
+ //no subdomain so we should add the default subdomain unless it is localhost
+ var gotoSubdomain = string.Empty;
+ if (!host.Equals("localhost", StringComparison.InvariantCultureIgnoreCase) && !string.IsNullOrWhiteSpace(defaultSubdomain))
+ {
+ gotoSubdomain = string.Format("{0}.", defaultSubdomain);
+ }
+ baseUrl = string.Format("{0}://{1}{2}", scheme, gotoSubdomain, host);
+ }
+
+ //not using a standard port so if the baseurl has a value then append on the port
+ if (!string.IsNullOrWhiteSpace(baseUrl) && !useDefaultPort)
+ {
+ baseUrl = string.Format("{0}:{1}", baseUrl, port);
+ }
+ }
+ return baseUrl;
+ }
+
+ private static string BuildUri(string baseUrl, string relativeUrl)
+ {
+ if (string.IsNullOrWhiteSpace(baseUrl) && string.IsNullOrWhiteSpace(relativeUrl))
+ {
+ return string.Empty;
+ }
+ if (string.IsNullOrWhiteSpace(baseUrl))
+ {
+ return relativeUrl;
+ }
+ if (string.IsNullOrWhiteSpace(relativeUrl))
+ {
+ return baseUrl;
+ }
+ if (!relativeUrl.StartsWith("/"))
+ {
+ relativeUrl = "/" + relativeUrl;
+ }
+ return string.Format("{0}{1}", baseUrl, relativeUrl);
+ }
+
+ public static RouteValueDictionary GetRouteValues(RouteValueDictionary routeValues)
+ {
+ return routeValues == null ? new RouteValueDictionary() : new RouteValueDictionary(routeValues);
+ }
+
+ public static RouteValueDictionary MergeRouteValues(string actionName, string controllerName,
+ RouteValueDictionary implicitRouteValues,
+ RouteValueDictionary routeValues,
+ bool includeImplicitMvcValues)
+ {
+ var routeValueDictionary = new RouteValueDictionary();
+ if (includeImplicitMvcValues)
+ {
+ object obj;
+ if (implicitRouteValues != null && implicitRouteValues.TryGetValue("action", out obj))
+ {
+ routeValueDictionary["action"] = obj;
+ }
+ if (implicitRouteValues != null && implicitRouteValues.TryGetValue("controller", out obj))
+ {
+ routeValueDictionary["controller"] = obj;
+ }
+ }
+ if (routeValues != null)
+ {
+ foreach (var keyValuePair in GetRouteValues(routeValues))
+ {
+ routeValueDictionary[keyValuePair.Key] = keyValuePair.Value;
+ }
+ }
+ if (actionName != null)
+ {
+ routeValueDictionary["action"] = actionName;
+ }
+ if (controllerName != null)
+ {
+ routeValueDictionary["controller"] = controllerName;
+ }
+ return routeValueDictionary;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 793277b..ee8a6d8 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -1,504 +1,505 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using AttributeRouting.Framework.Factories;
-using AttributeRouting.Helpers;
-
-namespace AttributeRouting.Framework
-{
- ///
- /// Creates objects according to the
- /// options set in implementations of .
- ///
- public class RouteBuilder
- {
- private readonly ConfigurationBase _configuration;
- private readonly IAttributeRouteFactory _routeFactory;
- private readonly IRouteConstraintFactory _routeConstraintFactory;
- private readonly IParameterFactory _parameterFactory;
-
- public RouteBuilder(ConfigurationBase configuration)
- {
- if (configuration == null) throw new ArgumentNullException("configuration");
-
- _configuration = configuration;
- _routeFactory = configuration.AttributeRouteFactory;
- _routeConstraintFactory = configuration.RouteConstraintFactory;
- _parameterFactory = configuration.ParameterFactory;
- }
-
- ///
- /// Yields all the routes to register in the route table.
- ///
- public IEnumerable BuildAllRoutes()
- {
- var routeReflector = new RouteReflector(_configuration);
- var routeSpecs = routeReflector.BuildRouteSpecifications().ToList();
-
- var mappedSubdomains = (from s in routeSpecs
- where s.Subdomain.HasValue()
- select s.Subdomain).Distinct().ToList();
-
- foreach (var routeSpec in routeSpecs)
- {
- foreach (var route in Build(routeSpec))
- {
- route.MappedSubdomains = mappedSubdomains;
- yield return route;
- }
- }
- }
-
- private IEnumerable Build(RouteSpecification routeSpec)
- {
- var defaults = CreateRouteDefaults(routeSpec);
- var constraints = CreateRouteConstraints(routeSpec);
- var dataTokens = CreateRouteDataTokens(routeSpec);
- var url = CreateRouteUrl(defaults, routeSpec);
-
- var routes = _routeFactory.CreateAttributeRoutes(url, defaults, constraints, dataTokens);
-
- foreach (var route in routes)
- {
- var routeName = CreateRouteName(routeSpec);
- if (routeName.HasValue())
- {
- route.RouteName = routeName;
- route.DataTokens.Add("routeName", routeName);
- }
-
- route.Translations = CreateRouteTranslations(routeSpec);
- route.Subdomain = routeSpec.Subdomain;
- route.UseLowercaseRoute = routeSpec.UseLowercaseRoute;
- route.PreserveCaseForUrlParameters = routeSpec.PreserveCaseForUrlParameters;
- route.AppendTrailingSlash = routeSpec.AppendTrailingSlash;
-
- // Yield the default route first
- yield return route;
-
- // Then yield the translations
- if (route.Translations == null)
- yield break;
-
- foreach (var translation in route.Translations)
- {
- // Backreference the default route.
- translation.DefaultRouteContainer = route;
-
- yield return translation;
- }
- }
- }
-
- private string CreateRouteName(RouteSpecification routeSpec)
- {
- if (routeSpec.RouteName.HasValue())
- return routeSpec.RouteName;
-
- return _configuration.AutoGenerateRouteNames ? _configuration.RouteNameBuilder(routeSpec) : null;
- }
-
- private string CreateRouteUrl(IDictionary defaults, RouteSpecification routeSpec)
- {
- return CreateRouteUrl(routeSpec.RouteUrl,
- routeSpec.RoutePrefixUrl,
- routeSpec.AreaUrl,
- defaults,
- routeSpec);
- }
-
- private string CreateRouteUrl(string routeUrl, string routePrefix, string areaUrl, IDictionary defaults, RouteSpecification routeSpec)
- {
- var tokenizedUrl = BuildTokenizedUrl(routeUrl, routePrefix, areaUrl, routeSpec);
- var tokenizedPath = RemoveQueryString(tokenizedUrl);
- var detokenizedPath = DetokenizeUrl(tokenizedPath);
-
- var urlParameterNames = GetUrlParameterContents(detokenizedPath).ToList();
-
- var urlBuilder = new StringBuilder(detokenizedPath);
-
- // Replace {controller} URL param with default value.
- if (urlParameterNames.Any(n => n.ValueEquals("controller")))
- urlBuilder.Replace("{controller}", (string)defaults["controller"]);
-
- // Replace {action} URL param with default value.
- if (urlParameterNames.Any(n => n.ValueEquals("action")))
- urlBuilder.Replace("{action}", (string)defaults["action"]);
-
- // Explicitly defined area routes are not valid
- if (urlParameterNames.Any(n => n.ValueEquals("area")))
- throw new AttributeRoutingException(
- "{area} url parameters are not allowed. Specify the area name by using the RouteAreaAttribute.");
-
- // If we are lowercasing routes, then lowercase everything but the route params
- var lower = routeSpec.UseLowercaseRoute.GetValueOrDefault(_configuration.UseLowercaseRoutes);
- if (lower)
- {
- for (var i = 0; i < urlBuilder.Length; i++)
- {
- var c = urlBuilder[i];
- if (Char.IsUpper(c))
- {
- urlBuilder[i] = Char.ToLower(c);
- }
- else if (c == '{')
- {
- while (urlBuilder[i] != '}' && i < urlBuilder.Length)
- i++;
- }
- }
- }
-
- return urlBuilder.ToString().Trim('/');
- }
-
- private IDictionary CreateRouteDefaults(RouteSpecification routeSpec)
- {
- var defaults = new Dictionary
- {
- { "controller", routeSpec.ControllerName },
- { "action", routeSpec.ActionName }
- };
-
- var urlParameters = GetUrlParameterContents(routeSpec.RouteUrl).ToList();
-
- // Inspect the url for optional parameters, specified with a trailing ?
- foreach (var parameter in urlParameters.Where(p => p.EndsWith("?")))
- {
- var parameterName = parameter.TrimEnd('?');
-
- if (parameterName.Contains(':'))
- parameterName = parameterName.Substring(0, parameterName.IndexOf(':'));
-
- if (defaults.ContainsKey(parameterName))
- continue;
-
- defaults.Add(parameterName, _parameterFactory.Optional());
- }
-
- // Inline defaults
- foreach (var parameter in urlParameters.Where(p => p.Contains('=')))
- {
- var indexOfEquals = parameter.IndexOf('=');
- var parameterName = parameter.Substring(0, indexOfEquals);
-
- if (parameterName.Contains(':'))
- parameterName = parameterName.Substring(0, parameterName.IndexOf(':'));
-
- if (defaults.ContainsKey(parameterName))
- continue;
-
- var defaultValue = parameter.Substring(indexOfEquals + 1, parameter.Length - indexOfEquals - 1);
- defaults.Add(parameterName, defaultValue);
- }
-
- return defaults;
- }
-
- private IDictionary CreateRouteConstraints(RouteSpecification routeSpec)
- {
- var constraints = new Dictionary();
-
- // Default constraints
- if (routeSpec.HttpMethods.Any())
- constraints.Add("inboundHttpMethod", _routeConstraintFactory.CreateInboundHttpMethodConstraint(routeSpec.HttpMethods));
-
- // Work from a complete, tokenized url; ie: support constraints in area urls, route prefix urls, and route urls.
- var tokenizedUrl = BuildTokenizedUrl(routeSpec.RouteUrl, routeSpec.RoutePrefixUrl, routeSpec.AreaUrl, routeSpec);
- var urlParameters = GetUrlParameterContents(tokenizedUrl).ToList();
-
- // Need to keep track of query params.
- // Can do this by detokenizing URL (which strips query),
- // and then taking all the URL parameters except those from the path part of the URL.
- var pathOnlyUrl = RemoveQueryString(tokenizedUrl);
- var pathOnlyUrlParameters = GetUrlParameterContents(pathOnlyUrl);
- var queryStringParameters = urlParameters.Except(pathOnlyUrlParameters).ToList();
-
- // Inline constraints
- foreach (var parameter in urlParameters)
- {
- // Keep track of whether this param is optional or in the querystring,
- // because we wrap the final constraint if so.
- var parameterIsOptional = parameter.EndsWith("?");
- var parameterIsInQueryString = queryStringParameters.Contains(parameter);
-
- // If this is a path parameter and doesn't have a constraint, then skip it.
- if (!parameterIsInQueryString && !parameter.Contains(":"))
- continue;
-
- // Strip off everything related to defaults.
- var cleanParameter = parameter.TrimEnd('?').Split('=').FirstOrDefault();
-
- var sections = cleanParameter.SplitAndTrim(":");
- var parameterName = sections.First();
-
- // Do not override default or legacy inline constraints
- if (constraints.ContainsKey(parameterName))
- continue;
-
- // Add constraints for each inline definition
- var inlineConstraints = new List
+
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests.Web/packages.config b/src/AttributeRouting.Tests.Web/packages.config
index ba9ece5..76f6b20 100644
--- a/src/AttributeRouting.Tests.Web/packages.config
+++ b/src/AttributeRouting.Tests.Web/packages.config
@@ -5,5 +5,5 @@
-
+
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
index 2d38ede..6b2456d 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
+++ b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj
@@ -44,24 +44,25 @@
- ..\packages\Newtonsoft.Json.4.5.8\lib\net40\Newtonsoft.Json.dll
+ False
+ ..\..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll
- ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll
+ ..\..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll
- ..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll
+ ..\..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll
- ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll
+ ..\..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll
- ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll
+ ..\..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll
- ..\packages\Microsoft.AspNet.WebApi.SelfHost.4.0.20710.0\lib\net40\System.Web.Http.SelfHost.dll
+ ..\..\packages\Microsoft.AspNet.WebApi.SelfHost.4.0.20918.0\lib\net40\System.Web.Http.SelfHost.dll
diff --git a/src/AttributeRouting.Web.Http.SelfHost/packages.config b/src/AttributeRouting.Web.Http.SelfHost/packages.config
index 1ba9e00..6c98029 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/packages.config
+++ b/src/AttributeRouting.Web.Http.SelfHost/packages.config
@@ -2,7 +2,7 @@
-
+
-
+
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj b/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj
index 4251784..61a1673 100644
--- a/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj
+++ b/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj
@@ -45,28 +45,29 @@
True
- ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll
+ ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll
- ..\packages\Newtonsoft.Json.4.5.8\lib\net40\Newtonsoft.Json.dll
+ False
+ ..\..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll
- ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll
+ ..\..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll
- ..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll
+ ..\..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll
- ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll
+ ..\..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll
- ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll
+ ..\..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll
- ..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.20710.0\lib\net40\System.Web.Http.WebHost.dll
+ ..\..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.20710.0\lib\net40\System.Web.Http.WebHost.dll
diff --git a/src/AttributeRouting.Web.Http.WebHost/packages.config b/src/AttributeRouting.Web.Http.WebHost/packages.config
index ba9ece5..76f6b20 100644
--- a/src/AttributeRouting.Web.Http.WebHost/packages.config
+++ b/src/AttributeRouting.Web.Http.WebHost/packages.config
@@ -5,5 +5,5 @@
-
+
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj b/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
index 7a09a9f..ceaee36 100644
--- a/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
+++ b/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
@@ -44,22 +44,23 @@
- ..\packages\Newtonsoft.Json.4.5.8\lib\net40\Newtonsoft.Json.dll
+ False
+ ..\..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll
- ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll
+ ..\..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll
- ..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll
+ ..\..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll
- ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll
+ ..\..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll
- ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll
+ ..\..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll
diff --git a/src/AttributeRouting.Web.Http/packages.config b/src/AttributeRouting.Web.Http/packages.config
index 6551089..5973fb4 100644
--- a/src/AttributeRouting.Web.Http/packages.config
+++ b/src/AttributeRouting.Web.Http/packages.config
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs b/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs
index 8f233ac..a62869a 100644
--- a/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs
+++ b/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs
@@ -11,77 +11,23 @@ namespace AttributeRouting.Web.Mvc.Extensions
{
public static class UrlHelperExtensions
{
- public static string SubdomainAction(this UrlHelper urlHelper, string actionName,
- RouteValueDictionary routeValues)
- {
- string baseUrl = GetDomainBase(urlHelper, null, actionName, null, routeValues);
- return BuildUri(baseUrl, urlHelper.Action(actionName, routeValues));
- }
-
- public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName,
- string areaName = "")
- {
- var routeValues = new RouteValueDictionary {{"area", areaName}};
- string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName, routeValues);
- return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues));
- }
-
- public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName,
- object routeValues)
- {
- string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName,
- new RouteValueDictionary(routeValues));
- return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues));
- }
-
- public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName,
- RouteValueDictionary routeValues)
- {
- string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName, routeValues);
+ public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName = null, object routeValues = null, string protocol = null)
+ {
+ var routeValueDictionary = new RouteValueDictionary(routeValues);
+ var baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName, routeValueDictionary, protocol);
return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues));
- }
-
- public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName,
- object routeValues, string protocol)
- {
- string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName,
- new RouteValueDictionary(routeValues), protocol);
- return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues));
- }
-
- public static string SubdomainRouteUrl(this UrlHelper urlHelper, object routeValues)
+ }
+
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, object routeValues, string protocol = null)
{
- return SubdomainRouteUrl(urlHelper, null, routeValues);
+ return SubdomainRouteUrl(urlHelper, null, routeValues, protocol);
}
- public static string SubdomainRouteUrl(this UrlHelper urlHelper, RouteValueDictionary routeValues)
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName, object routeValues = null, string protocol = null)
{
- return SubdomainRouteUrl(urlHelper, null, routeValues);
- }
-
- public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName)
- {
- return SubdomainRouteUrl(urlHelper, routeName, (object) null);
- }
-
- public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName, object routeValues)
- {
- return SubdomainRouteUrl(urlHelper, routeName, routeValues, null);
- }
-
- public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName,
- RouteValueDictionary routeValues)
- {
- string baseUrl = GetDomainBase(urlHelper, routeName, null, null, routeValues);
- return BuildUri(baseUrl, urlHelper.RouteUrl(routeName, routeValues));
- }
-
- public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName, object routeValues,
- string protocol)
- {
- var r = new RouteValueDictionary(routeValues);
- string baseUrl = GetDomainBase(urlHelper, routeName, null, null, r, protocol);
- return BuildUri(baseUrl, urlHelper.RouteUrl(routeName, routeValues));
+ var routeValueDictionary = new RouteValueDictionary(routeValues);
+ var baseUrl = GetDomainBase(urlHelper, routeName, null, null, routeValueDictionary, protocol);
+ return BuildUri(baseUrl, urlHelper.RouteUrl(routeName, routeValueDictionary));
}
///
@@ -94,134 +40,130 @@ public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeNam
/// The route values.
/// The schema.
///
- private static string GetDomainBase(UrlHelper urlHelper, string routeName, string actionName,
- string controllerName, RouteValueDictionary routeValues,
- string schema = null)
+ private static string GetDomainBase(UrlHelper urlHelper, string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, string schema = null)
{
- //baseUrl is the return value which by default is an empty string
- string baseUrl = string.Empty;
+ // baseUrl is the return value which by default is an empty string.
+ var baseUrl = String.Empty;
- //just a shortcut variable so we don't have to have this the below line eight million times
- Uri currentUrl = urlHelper.RequestContext.HttpContext.Request.Url;
+ // Just a shortcut variable so we don't have to have this the below line eight million times.
+ var currentUrl = urlHelper.RequestContext.HttpContext.Request.Url;
//get the desired route using a copy of MS internal methods
- RouteValueDictionary values = MergeRouteValues(actionName, controllerName, urlHelper.RequestContext.RouteData.Values, routeValues, true);
- VirtualPathData virtualPathForArea = urlHelper.RouteCollection.GetVirtualPathForArea(urlHelper.RequestContext, routeName, values);
+ var values = MergeRouteValues(actionName, controllerName, urlHelper.RequestContext.RouteData.Values, routeValues, true);
+ var virtualPathForArea = urlHelper.RouteCollection.GetVirtualPathForArea(urlHelper.RequestContext, routeName, values);
if (virtualPathForArea == null)
{
return baseUrl;
}
- //if not a AttributeRoute or the current url is funny then nothing we can do so move on
+ // If not an AttributeRoute or the current url is funny then nothing we can do so move on.
var route = virtualPathForArea.Route as IAttributeRoute;
- if (route != null && currentUrl != null && !string.IsNullOrWhiteSpace(currentUrl.OriginalString))
+ if (route != null && currentUrl != null && currentUrl.OriginalString.HasValue())
{
- //get the current domain via the current Uri.
- string host = currentUrl.GetLeftPart(UriPartial.Authority).Replace(currentUrl.GetLeftPart(UriPartial.Scheme), string.Empty);
-
+ // Get the current domain via the current Uri.
+ var host = currentUrl.GetLeftPart(UriPartial.Authority).Replace(currentUrl.GetLeftPart(UriPartial.Scheme), string.Empty);
+
+ // If the port exists in the host remove it so that we don't run into trouble with the IPAddress parsing
IPAddress ip;
- //if the port exists in the host remove it so that we don't run into trouble with the IPAddress parsing
if (host.Contains(":"))
{
host = host.Substring(0, host.IndexOf(":", StringComparison.Ordinal));
}
- //if an ip then no point in building a subdomain for it
+ // If an ip then no point in building a subdomain for it
if (IPAddress.TryParse(host, out ip))
{
return string.Empty;
}
- //save the current host for comparisons later
- string currentHost = host;
+ // Save the current host for comparisons later
+ var currentHost = host;
- //which protocol schema to use. i.e. http, https
- string scheme = schema ?? currentUrl.Scheme;
+ // Which protocol schema to use. i.e. http, https
+ var scheme = schema ?? currentUrl.Scheme;
- //what is the current port. needed if non-standard
- int port = currentUrl.Port;
+ // What is the current port. needed if non-standard
+ var port = currentUrl.Port;
- //is the port a standard port?
- bool useDefaultPort = port == 80 || port == 443;
+ // Is the port a standard port?
+ var useDefaultPort = port == 80 || port == 443;
- //need the default subdomain incase we are going from one subdomain method to a non-subdomain method
- string defaultSubdomain = string.Empty;
- if (route.DataTokens.Any(x => x.Key.Equals("defaultSubdomain")))
+ // Need the default subdomain incase we are going from one subdomain method to a non-subdomain method
+ var defaultSubdomain = String.Empty;
+ object defaultSubdomainValue;
+ if (route.DataTokens.TryGetValue("defaultSubdomain", out defaultSubdomainValue))
{
- defaultSubdomain = route.DataTokens["defaultSubdomain"].ToString();
+ defaultSubdomain = defaultSubdomainValue.ToString();
}
- //if the host contains a dot we need to remove the subdomain if it is in the list of ones to remove
+ // If the host contains a dot we need to remove the subdomain if it is in the list of ones to remove
if (host.Contains("."))
{
- //get all registered subdomains
- List subdomains =
- urlHelper.RouteCollection.Where(x => x is IAttributeRoute)
- .Cast()
- .Where(x => x.Subdomain.HasValue())
- .Select(x => x.Subdomain)
- .Distinct()
- .ToList();
-
- //also add the default subdomain from the current route
+ // Get all registered subdomains
+ var subdomains = urlHelper.RouteCollection
+ .Where(x => x is IAttributeRoute)
+ .Cast()
+ .Where(x => x.Subdomain.HasValue())
+ .Select(x => x.Subdomain)
+ .Distinct()
+ .ToList();
+
+ // Also add the default subdomain from the current route
if (!string.IsNullOrWhiteSpace(defaultSubdomain) && !subdomains.Contains(defaultSubdomain))
{
subdomains.Add(defaultSubdomain);
}
- //strips subdomain information off of current (if matching a current one)
- string subDomainSection = host.Split('.')[0];
- foreach (
- string subdomain in
- subdomains.Where(
- subdomain =>
- subDomainSection.Equals(subdomain, StringComparison.InvariantCultureIgnoreCase)))
+ // Strips subdomain information off of current (if matching a current one)
+ var subdomainSection = host.Split('.')[0];
+ var subdomain = subdomains.FirstOrDefault(subdomainSection.ValueEquals);
+ if (subdomain.HasValue())
{
- host = host.Replace(string.Format("{0}.", subdomain), string.Empty);
- break;
+ host = host.Replace(subdomain + ".", null);
}
}
- //if not a subdomain then don't build the url. instead build it to the default subdomain
- if (!string.IsNullOrWhiteSpace(route.Subdomain))
+ // If not a subdomain then don't build the url; instead build it to the default subdomain.
+ if (route.Subdomain.HasValue())
{
- //if host already starts with subdomain then skip building the url
+ // If host already starts with subdomain then skip building the url
if (!currentHost.StartsWith(route.Subdomain))
{
- baseUrl = string.Format("{0}://{1}.{2}", scheme, route.Subdomain, host);
+ baseUrl = "{0}://{1}.{2}".FormatWith(scheme, route.Subdomain, host);
}
}
else
{
- //no subdomain so we should add the default subdomain unless it is localhost
- var gotoSubdomain = string.Empty;
- if (!host.Equals("localhost", StringComparison.InvariantCultureIgnoreCase) && !string.IsNullOrWhiteSpace(defaultSubdomain))
+ // No subdomain so we should add the default subdomain unless it is localhost
+ var subdomain = string.Empty;
+ if (!host.ValueEquals("localhost") && defaultSubdomain.HasValue())
{
- gotoSubdomain = string.Format("{0}.", defaultSubdomain);
+ subdomain = defaultSubdomain + ".";
}
- baseUrl = string.Format("{0}://{1}{2}", scheme, gotoSubdomain, host);
+ baseUrl = "{0}://{1}{2}".FormatWith(scheme, subdomain, host);
}
//not using a standard port so if the baseurl has a value then append on the port
- if (!string.IsNullOrWhiteSpace(baseUrl) && !useDefaultPort)
+ if (baseUrl.HasValue() && !useDefaultPort)
{
- baseUrl = string.Format("{0}:{1}", baseUrl, port);
+ baseUrl = "{0}:{1}".FormatWith(baseUrl, port);
}
}
+
return baseUrl;
}
private static string BuildUri(string baseUrl, string relativeUrl)
{
- if (string.IsNullOrWhiteSpace(baseUrl) && string.IsNullOrWhiteSpace(relativeUrl))
+ if (baseUrl.HasNoValue() && relativeUrl.HasNoValue())
{
- return string.Empty;
+ return String.Empty;
}
- if (string.IsNullOrWhiteSpace(baseUrl))
+ if (baseUrl.HasNoValue())
{
return relativeUrl;
}
- if (string.IsNullOrWhiteSpace(relativeUrl))
+ if (relativeUrl.HasNoValue())
{
return baseUrl;
}
@@ -229,18 +171,10 @@ private static string BuildUri(string baseUrl, string relativeUrl)
{
relativeUrl = "/" + relativeUrl;
}
- return string.Format("{0}{1}", baseUrl, relativeUrl);
- }
-
- public static RouteValueDictionary GetRouteValues(RouteValueDictionary routeValues)
- {
- return routeValues == null ? new RouteValueDictionary() : new RouteValueDictionary(routeValues);
+ return "{0}{1}".FormatWith(baseUrl, relativeUrl);
}
- public static RouteValueDictionary MergeRouteValues(string actionName, string controllerName,
- RouteValueDictionary implicitRouteValues,
- RouteValueDictionary routeValues,
- bool includeImplicitMvcValues)
+ public static RouteValueDictionary MergeRouteValues(string actionName, string controllerName, RouteValueDictionary implicitRouteValues, RouteValueDictionary routeValues, bool includeImplicitMvcValues)
{
var routeValueDictionary = new RouteValueDictionary();
if (includeImplicitMvcValues)
@@ -257,7 +191,8 @@ public static RouteValueDictionary MergeRouteValues(string actionName, string co
}
if (routeValues != null)
{
- foreach (var keyValuePair in GetRouteValues(routeValues))
+ var newRouteValueDictionary = new RouteValueDictionary(routeValues);
+ foreach (var keyValuePair in newRouteValueDictionary)
{
routeValueDictionary[keyValuePair.Key] = keyValuePair.Value;
}
diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs
index 29fcad0..2ddb08b 100644
--- a/src/SharedAssemblyInfo.cs
+++ b/src/SharedAssemblyInfo.cs
@@ -1,13 +1,13 @@
-using System;
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-[assembly: ComVisible(false)]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("AttributeRouting")]
-[assembly: AssemblyCopyright("Copyright 2010-2012 Tim McCall")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("3.4.1")]
-[assembly: AssemblyFileVersion("3.4.1")]
-[assembly: AssemblyInformationalVersion("3.4.1")]
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: ComVisible(false)]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AttributeRouting")]
+[assembly: AssemblyCopyright("Copyright 2010-2013 Tim McCall")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyVersion("3.4.1")]
+[assembly: AssemblyFileVersion("3.4.1")]
+[assembly: AssemblyInformationalVersion("3.4.1")]
[assembly: AssemblyConfiguration("Release")]
From 5e1f2ffffb974c3111e6849fd478ea283fc84dda Mon Sep 17 00:00:00 2001
From: Shawn Carr
Date: Fri, 1 Feb 2013 14:46:57 -0500
Subject: [PATCH 33/97] More Testing of the Subdomain Routing events
---
.../Extensions/UrlHelperExtensionTests.cs | 265 +++++++++++++++++-
1 file changed, 262 insertions(+), 3 deletions(-)
diff --git a/src/AttributeRouting.Specs/Tests/Extensions/UrlHelperExtensionTests.cs b/src/AttributeRouting.Specs/Tests/Extensions/UrlHelperExtensionTests.cs
index dfb9810..327d877 100644
--- a/src/AttributeRouting.Specs/Tests/Extensions/UrlHelperExtensionTests.cs
+++ b/src/AttributeRouting.Specs/Tests/Extensions/UrlHelperExtensionTests.cs
@@ -24,6 +24,8 @@ private UrlHelper GetUrlHelper(RouteCollection routes, string host, string schem
return new UrlHelper(new RequestContext(httpContextMock.Object, new RouteData()), routes);
}
+ #region Action
+
[Test]
public void Local_Host_Action_Will_Have_A_Subdomain_Prepended()
{
@@ -37,7 +39,6 @@ public void Local_Host_Action_Will_Have_A_Subdomain_Prepended()
Assert.AreEqual("http://users.localhost/", path);
}
-
[Test]
public void Top_Level_Domain_Action_Will_Have_A_Subdomain_Prepended()
{
@@ -221,7 +222,7 @@ public void HTTPS_URL_Will_Change_To_HTTP()
routes.MapAttributeRoutes(config => config.AddRoutesFromController());
var helper = GetUrlHelper(routes, "localhost", "https");
Assert.That(helper, Is.Not.Null);
- var path = helper.SubdomainAction("Index", "Subdomain", new { area = "Users" }, "http");
+ var path = helper.SubdomainAction("Index", "Subdomain", new {area = "Users"}, "http");
Console.WriteLine(path);
Assert.AreEqual("http://users.localhost/", path);
}
@@ -234,9 +235,267 @@ public void Host_With_Port_Will_Have_A_Subdomain_Prepended()
routes.MapAttributeRoutes(config => config.AddRoutesFromController());
var helper = GetUrlHelper(routes, "example.com:81");
Assert.That(helper, Is.Not.Null);
- var path = helper.SubdomainAction("Index", "Subdomain", new { area = "Users" });
+ var path = helper.SubdomainAction("Index", "Subdomain", new {area = "Users"});
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.example.com:81/", path);
+ }
+
+ #endregion
+
+ #region RouteURL
+
+ [Test]
+ public void RouteURL_Local_Host_Action_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AutoGenerateRouteNames = true;
+ });
+ var helper = GetUrlHelper(routes, "localhost");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("Users_Subdomain_Index");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.localhost/", path);
+ }
+
+
+ [Test]
+ public void RouteURL_Top_Level_Domain_Action_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AutoGenerateRouteNames = true;
+ });
+ var helper = GetUrlHelper(routes, "example.com");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("Users_Subdomain_Index");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.example.com/", path);
+ }
+
+ [Test]
+ public void RouteURL_Second_Level_Domain_Action_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AutoGenerateRouteNames = true;
+ });
+ var helper = GetUrlHelper(routes, "example.co.uk");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("Users_Subdomain_Index");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.example.co.uk/", path);
+ }
+
+ [Test]
+ public void RouteURL_Top_Level_Domain_With_Subdomain_Action_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ config.AutoGenerateRouteNames = true;
+ });
+ var helper = GetUrlHelper(routes, "private.example.com");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("Users_Subdomain_Index");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.example.com/", path);
+ }
+
+ [Test]
+ public void RouteURL_Second_Level_Domain_With_Subdomain_Action_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ config.AutoGenerateRouteNames = true;
+ });
+ var helper = GetUrlHelper(routes, "private.example.co.ok");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("Users_Subdomain_Index");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.example.co.ok/", path);
+ }
+
+ [Test]
+ public void RouteURL_Current_Subdomain_Action_To_Same_Subdomain_Will_Only_Be_The_Relative_Url()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AutoGenerateRouteNames = true;
+ });
+ var helper = GetUrlHelper(routes, "users.example.com");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("Users_Subdomain_Index");
+ Console.WriteLine(path);
+ Assert.AreEqual("/", path);
+ }
+
+ [Test]
+ public void RouteURL_Current_Subdomain_Action_To_No_Subdomain_And_Default_Subdomain_Will_Return_Full_Url()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ config.AutoGenerateRouteNames = true;
+ });
+ var helper = GetUrlHelper(routes, "users.example.com");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("StandardUsage_AnyVerb");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://www.example.com/AnyVerb", path);
+ }
+
+ [Test]
+ public void RouteURL_Current_Subdomain_Action_To_No_Subdomain_And_Custom_Default_Subdomain_Will_Return_Full_Url()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ config.AutoGenerateRouteNames = true;
+ config.DefaultSubdomain = "xyz";
+ });
+ var helper = GetUrlHelper(routes, "users.example.com");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("StandardUsage_AnyVerb");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://xyz.example.com/AnyVerb", path);
+ }
+
+ [Test]
+ public void RouteURL_Current_Subdomain_Action_To_No_Subdomain_And_No_Default_Subdomain_Will_Return_Full_Url()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ config.DefaultSubdomain = "";
+ config.AutoGenerateRouteNames = true;
+ });
+ var helper = GetUrlHelper(routes, "users.example.com");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("StandardUsage_AnyVerb");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://example.com/AnyVerb", path);
+ }
+
+ [Test]
+ public void RouteURL_Local_Host_With_Subdomain_Action_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ config.AutoGenerateRouteNames = true;
+ });
+ var helper = GetUrlHelper(routes, "private.localhost");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("Users_Subdomain_Index");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.localhost/", path);
+ }
+
+
+ [Test]
+ public void
+ RouteURL_Local_Host_With_Subdomain_Action_To_Non_Area_Method_Will_Return_Full_Url_Without_Default_Subdomain()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddRoutesFromController();
+ config.AutoGenerateRouteNames = true;
+ });
+ var helper = GetUrlHelper(routes, "users.localhost");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("StandardUsage_Index");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://localhost/", path);
+ }
+
+
+ [Test]
+ public void RouteURL_HTTP_URL_Will_Change_To_HTTPS()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AutoGenerateRouteNames = true;
+ });
+ var helper = GetUrlHelper(routes, "localhost");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("Users_Subdomain_Index", null, "https");
+ Console.WriteLine(path);
+ Assert.AreEqual("https://users.localhost/", path);
+ }
+
+ [Test]
+ public void RouteURL_HTTPS_URL_Will_Change_To_HTTP()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AutoGenerateRouteNames = true;
+ });
+ var helper = GetUrlHelper(routes, "localhost", "https");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("Users_Subdomain_Index", null, "http");
+ Console.WriteLine(path);
+ Assert.AreEqual("http://users.localhost/", path);
+ }
+
+ [Test]
+ public void RouteURL_Host_With_Port_Will_Have_A_Subdomain_Prepended()
+ {
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AutoGenerateRouteNames = true;
+ });
+ var helper = GetUrlHelper(routes, "example.com:81");
+ Assert.That(helper, Is.Not.Null);
+ var path = helper.SubdomainRouteUrl("Users_Subdomain_Index");
Console.WriteLine(path);
Assert.AreEqual("http://users.example.com:81/", path);
}
+
+ #endregion
+
}
}
\ No newline at end of file
From 886dce739cc07f1710e1c7cb5e5f4764b4dd6357 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Wed, 13 Feb 2013 12:55:32 -0700
Subject: [PATCH 34/97] #192 - fixed bug that caused optional params in asp.net
web api routes to fail.
---
.../HttpRouteCollectionExtensions.cs | 28 ++++---------------
1 file changed, 6 insertions(+), 22 deletions(-)
diff --git a/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs b/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
index d8d2abb..2697b79 100644
--- a/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
@@ -50,28 +50,12 @@ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, HttpW
routes.MapHttpAttributeRoutesInternal(configuration);
}
- private static void MapHttpAttributeRoutesInternal(this HttpRouteCollection routes, HttpWebConfiguration configuration)
- {
- var generatedRoutes = new RouteBuilder(configuration).BuildAllRoutes().Cast().ToList();
-
- // If providing a custom IRouteHandler via config, add the routes to the RouteCollection.
- // Have to do this because the HttpRoutes do not support the functionality.
- var routeHandler = configuration.RouteHandlerFactory();
- if (routeHandler != null)
- {
- var mvcRoutes = RouteTable.Routes;
- generatedRoutes.ForEach(r =>
- {
- var mvcRoute = mvcRoutes.MapHttpRoute(r.RouteName, r.Url, r.Defaults, r.Constraints, r.Handler);
- mvcRoute.DataTokens = new RouteValueDictionary(r.DataTokens);
- mvcRoute.RouteHandler = routeHandler;
- });
- }
- else
- {
- // Otherwise, add them to the HttpRouteCollection.
- generatedRoutes.ForEach(r => routes.Add(r.RouteName, r));
- }
+ private static void MapHttpAttributeRoutesInternal(this HttpRouteCollection routes, HttpWebConfiguration configuration)
+ {
+ new RouteBuilder(configuration).BuildAllRoutes()
+ .Cast()
+ .ToList()
+ .ForEach(r => routes.Add(r.RouteName, r));
}
}
}
\ No newline at end of file
From 275b208a67da4fbb254fce7ad340747f29813850 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Wed, 13 Feb 2013 13:04:55 -0700
Subject: [PATCH 35/97] Restored original subdomain url helpers.
---
.../Extensions/UrlHelperExtensions.cs | 479 ++++++++++--------
1 file changed, 272 insertions(+), 207 deletions(-)
diff --git a/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs b/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs
index a62869a..239067b 100644
--- a/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs
+++ b/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs
@@ -1,211 +1,276 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Web.Mvc;
-using System.Web.Routing;
-using AttributeRouting.Framework;
-using AttributeRouting.Helpers;
-
-namespace AttributeRouting.Web.Mvc.Extensions
-{
- public static class UrlHelperExtensions
- {
- public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName = null, object routeValues = null, string protocol = null)
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Web.Mvc;
+using System.Web.Routing;
+using AttributeRouting.Framework;
+using AttributeRouting.Helpers;
+
+namespace AttributeRouting.Web.Mvc.Extensions
+{
+ public static class UrlHelperExtensions
+ {
+ public static string SubdomainAction(this UrlHelper urlHelper, string actionName,
+ RouteValueDictionary routeValues)
+ {
+ string baseUrl = GetDomainBase(urlHelper, null, actionName, null, routeValues);
+ return BuildUri(baseUrl, urlHelper.Action(actionName, routeValues));
+ }
+
+ public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName,
+ string areaName = "")
{
- var routeValueDictionary = new RouteValueDictionary(routeValues);
- var baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName, routeValueDictionary, protocol);
- return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues));
+ var routeValues = new RouteValueDictionary { { "area", areaName } };
+ string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName, routeValues);
+ return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues));
}
- public static string SubdomainRouteUrl(this UrlHelper urlHelper, object routeValues, string protocol = null)
- {
- return SubdomainRouteUrl(urlHelper, null, routeValues, protocol);
- }
-
- public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName, object routeValues = null, string protocol = null)
- {
- var routeValueDictionary = new RouteValueDictionary(routeValues);
- var baseUrl = GetDomainBase(urlHelper, routeName, null, null, routeValueDictionary, protocol);
- return BuildUri(baseUrl, urlHelper.RouteUrl(routeName, routeValueDictionary));
- }
-
- ///
- /// Gets the domain base url.
- ///
- /// The URL helper.
- /// Name of the route.
- /// Name of the action.
- /// Name of the controller.
- /// The route values.
- /// The schema.
- ///
- private static string GetDomainBase(UrlHelper urlHelper, string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, string schema = null)
- {
- // baseUrl is the return value which by default is an empty string.
- var baseUrl = String.Empty;
-
- // Just a shortcut variable so we don't have to have this the below line eight million times.
- var currentUrl = urlHelper.RequestContext.HttpContext.Request.Url;
-
- //get the desired route using a copy of MS internal methods
- var values = MergeRouteValues(actionName, controllerName, urlHelper.RequestContext.RouteData.Values, routeValues, true);
- var virtualPathForArea = urlHelper.RouteCollection.GetVirtualPathForArea(urlHelper.RequestContext, routeName, values);
- if (virtualPathForArea == null)
- {
- return baseUrl;
- }
-
- // If not an AttributeRoute or the current url is funny then nothing we can do so move on.
- var route = virtualPathForArea.Route as IAttributeRoute;
- if (route != null && currentUrl != null && currentUrl.OriginalString.HasValue())
- {
- // Get the current domain via the current Uri.
- var host = currentUrl.GetLeftPart(UriPartial.Authority).Replace(currentUrl.GetLeftPart(UriPartial.Scheme), string.Empty);
-
- // If the port exists in the host remove it so that we don't run into trouble with the IPAddress parsing
- IPAddress ip;
- if (host.Contains(":"))
- {
- host = host.Substring(0, host.IndexOf(":", StringComparison.Ordinal));
- }
-
- // If an ip then no point in building a subdomain for it
- if (IPAddress.TryParse(host, out ip))
- {
- return string.Empty;
- }
-
- // Save the current host for comparisons later
- var currentHost = host;
-
- // Which protocol schema to use. i.e. http, https
- var scheme = schema ?? currentUrl.Scheme;
-
- // What is the current port. needed if non-standard
- var port = currentUrl.Port;
-
- // Is the port a standard port?
- var useDefaultPort = port == 80 || port == 443;
-
- // Need the default subdomain incase we are going from one subdomain method to a non-subdomain method
- var defaultSubdomain = String.Empty;
- object defaultSubdomainValue;
- if (route.DataTokens.TryGetValue("defaultSubdomain", out defaultSubdomainValue))
- {
- defaultSubdomain = defaultSubdomainValue.ToString();
- }
-
- // If the host contains a dot we need to remove the subdomain if it is in the list of ones to remove
- if (host.Contains("."))
- {
- // Get all registered subdomains
- var subdomains = urlHelper.RouteCollection
- .Where(x => x is IAttributeRoute)
- .Cast()
- .Where(x => x.Subdomain.HasValue())
- .Select(x => x.Subdomain)
- .Distinct()
- .ToList();
-
- // Also add the default subdomain from the current route
- if (!string.IsNullOrWhiteSpace(defaultSubdomain) && !subdomains.Contains(defaultSubdomain))
- {
- subdomains.Add(defaultSubdomain);
- }
-
- // Strips subdomain information off of current (if matching a current one)
- var subdomainSection = host.Split('.')[0];
- var subdomain = subdomains.FirstOrDefault(subdomainSection.ValueEquals);
- if (subdomain.HasValue())
- {
- host = host.Replace(subdomain + ".", null);
- }
- }
-
- // If not a subdomain then don't build the url; instead build it to the default subdomain.
- if (route.Subdomain.HasValue())
- {
- // If host already starts with subdomain then skip building the url
- if (!currentHost.StartsWith(route.Subdomain))
- {
- baseUrl = "{0}://{1}.{2}".FormatWith(scheme, route.Subdomain, host);
- }
- }
- else
- {
- // No subdomain so we should add the default subdomain unless it is localhost
- var subdomain = string.Empty;
- if (!host.ValueEquals("localhost") && defaultSubdomain.HasValue())
- {
- subdomain = defaultSubdomain + ".";
- }
- baseUrl = "{0}://{1}{2}".FormatWith(scheme, subdomain, host);
- }
-
- //not using a standard port so if the baseurl has a value then append on the port
- if (baseUrl.HasValue() && !useDefaultPort)
- {
- baseUrl = "{0}:{1}".FormatWith(baseUrl, port);
- }
- }
-
- return baseUrl;
- }
-
- private static string BuildUri(string baseUrl, string relativeUrl)
- {
- if (baseUrl.HasNoValue() && relativeUrl.HasNoValue())
- {
- return String.Empty;
- }
- if (baseUrl.HasNoValue())
- {
- return relativeUrl;
- }
- if (relativeUrl.HasNoValue())
- {
- return baseUrl;
- }
- if (!relativeUrl.StartsWith("/"))
- {
- relativeUrl = "/" + relativeUrl;
- }
- return "{0}{1}".FormatWith(baseUrl, relativeUrl);
- }
-
- public static RouteValueDictionary MergeRouteValues(string actionName, string controllerName, RouteValueDictionary implicitRouteValues, RouteValueDictionary routeValues, bool includeImplicitMvcValues)
- {
- var routeValueDictionary = new RouteValueDictionary();
- if (includeImplicitMvcValues)
- {
- object obj;
- if (implicitRouteValues != null && implicitRouteValues.TryGetValue("action", out obj))
- {
- routeValueDictionary["action"] = obj;
- }
- if (implicitRouteValues != null && implicitRouteValues.TryGetValue("controller", out obj))
- {
- routeValueDictionary["controller"] = obj;
- }
- }
- if (routeValues != null)
- {
- var newRouteValueDictionary = new RouteValueDictionary(routeValues);
- foreach (var keyValuePair in newRouteValueDictionary)
- {
- routeValueDictionary[keyValuePair.Key] = keyValuePair.Value;
- }
- }
- if (actionName != null)
- {
- routeValueDictionary["action"] = actionName;
- }
- if (controllerName != null)
- {
- routeValueDictionary["controller"] = controllerName;
- }
- return routeValueDictionary;
- }
- }
+ public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName,
+ object routeValues)
+ {
+ string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName,
+ new RouteValueDictionary(routeValues));
+ return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues));
+ }
+
+ public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName,
+ RouteValueDictionary routeValues)
+ {
+ string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName, routeValues);
+ return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues));
+ }
+
+ public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName,
+ object routeValues, string protocol)
+ {
+ string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName,
+ new RouteValueDictionary(routeValues), protocol);
+ return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues));
+ }
+
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, object routeValues)
+ {
+ return SubdomainRouteUrl(urlHelper, null, routeValues);
+ }
+
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, RouteValueDictionary routeValues)
+ {
+ return SubdomainRouteUrl(urlHelper, null, routeValues);
+ }
+
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName)
+ {
+ return SubdomainRouteUrl(urlHelper, routeName, (object)null);
+ }
+
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName, object routeValues)
+ {
+ return SubdomainRouteUrl(urlHelper, routeName, routeValues, null);
+ }
+
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName,
+ RouteValueDictionary routeValues)
+ {
+ string baseUrl = GetDomainBase(urlHelper, routeName, null, null, routeValues);
+ return BuildUri(baseUrl, urlHelper.RouteUrl(routeName, routeValues));
+ }
+
+ public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName, object routeValues,
+ string protocol)
+ {
+ var r = new RouteValueDictionary(routeValues);
+ string baseUrl = GetDomainBase(urlHelper, routeName, null, null, r, protocol);
+ return BuildUri(baseUrl, urlHelper.RouteUrl(routeName, routeValues));
+ }
+
+ ///
+ /// Gets the domain base url.
+ ///
+ /// The URL helper.
+ /// Name of the route.
+ /// Name of the action.
+ /// Name of the controller.
+ /// The route values.
+ /// The schema.
+ ///
+ private static string GetDomainBase(UrlHelper urlHelper, string routeName, string actionName,
+ string controllerName, RouteValueDictionary routeValues,
+ string schema = null)
+ {
+ //baseUrl is the return value which by default is an empty string
+ string baseUrl = string.Empty;
+
+ //just a shortcut variable so we don't have to have this the below line eight million times
+ Uri currentUrl = urlHelper.RequestContext.HttpContext.Request.Url;
+
+ //get the desired route using a copy of MS internal methods
+ RouteValueDictionary values = MergeRouteValues(actionName, controllerName, urlHelper.RequestContext.RouteData.Values, routeValues, true);
+ VirtualPathData virtualPathForArea = urlHelper.RouteCollection.GetVirtualPathForArea(urlHelper.RequestContext, routeName, values);
+ if (virtualPathForArea == null)
+ {
+ return baseUrl;
+ }
+
+ //if not a AttributeRoute or the current url is funny then nothing we can do so move on
+ var route = virtualPathForArea.Route as IAttributeRoute;
+ if (route != null && currentUrl != null && !string.IsNullOrWhiteSpace(currentUrl.OriginalString))
+ {
+ //get the current domain via the current Uri.
+ string host = currentUrl.GetLeftPart(UriPartial.Authority).Replace(currentUrl.GetLeftPart(UriPartial.Scheme), string.Empty);
+
+ IPAddress ip;
+ //if the port exists in the host remove it so that we don't run into trouble with the IPAddress parsing
+ if (host.Contains(":"))
+ {
+ host = host.Substring(0, host.IndexOf(":", StringComparison.Ordinal));
+ }
+
+ //if an ip then no point in building a subdomain for it
+ if (IPAddress.TryParse(host, out ip))
+ {
+ return string.Empty;
+ }
+
+ //save the current host for comparisons later
+ string currentHost = host;
+
+ //which protocol schema to use. i.e. http, https
+ string scheme = schema ?? currentUrl.Scheme;
+
+ //what is the current port. needed if non-standard
+ int port = currentUrl.Port;
+
+ //is the port a standard port?
+ bool useDefaultPort = port == 80 || port == 443;
+
+ //need the default subdomain incase we are going from one subdomain method to a non-subdomain method
+ string defaultSubdomain = string.Empty;
+ if (route.DataTokens.Any(x => x.Key.Equals("defaultSubdomain")))
+ {
+ defaultSubdomain = route.DataTokens["defaultSubdomain"].ToString();
+ }
+
+ //if the host contains a dot we need to remove the subdomain if it is in the list of ones to remove
+ if (host.Contains("."))
+ {
+ //get all registered subdomains
+ List subdomains =
+ urlHelper.RouteCollection.Where(x => x is IAttributeRoute)
+ .Cast()
+ .Where(x => x.Subdomain.HasValue())
+ .Select(x => x.Subdomain)
+ .Distinct()
+ .ToList();
+
+ //also add the default subdomain from the current route
+ if (!string.IsNullOrWhiteSpace(defaultSubdomain) && !subdomains.Contains(defaultSubdomain))
+ {
+ subdomains.Add(defaultSubdomain);
+ }
+
+ //strips subdomain information off of current (if matching a current one)
+ string subDomainSection = host.Split('.')[0];
+ foreach (
+ string subdomain in
+ subdomains.Where(
+ subdomain =>
+ subDomainSection.Equals(subdomain, StringComparison.InvariantCultureIgnoreCase)))
+ {
+ host = host.Replace(string.Format("{0}.", subdomain), string.Empty);
+ break;
+ }
+ }
+
+ //if not a subdomain then don't build the url. instead build it to the default subdomain
+ if (!string.IsNullOrWhiteSpace(route.Subdomain))
+ {
+ //if host already starts with subdomain then skip building the url
+ if (!currentHost.StartsWith(route.Subdomain))
+ {
+ baseUrl = string.Format("{0}://{1}.{2}", scheme, route.Subdomain, host);
+ }
+ }
+ else
+ {
+ //no subdomain so we should add the default subdomain unless it is localhost
+ var gotoSubdomain = string.Empty;
+ if (!host.Equals("localhost", StringComparison.InvariantCultureIgnoreCase) && !string.IsNullOrWhiteSpace(defaultSubdomain))
+ {
+ gotoSubdomain = string.Format("{0}.", defaultSubdomain);
+ }
+ baseUrl = string.Format("{0}://{1}{2}", scheme, gotoSubdomain, host);
+ }
+
+ //not using a standard port so if the baseurl has a value then append on the port
+ if (!string.IsNullOrWhiteSpace(baseUrl) && !useDefaultPort)
+ {
+ baseUrl = string.Format("{0}:{1}", baseUrl, port);
+ }
+ }
+ return baseUrl;
+ }
+
+ private static string BuildUri(string baseUrl, string relativeUrl)
+ {
+ if (string.IsNullOrWhiteSpace(baseUrl) && string.IsNullOrWhiteSpace(relativeUrl))
+ {
+ return string.Empty;
+ }
+ if (string.IsNullOrWhiteSpace(baseUrl))
+ {
+ return relativeUrl;
+ }
+ if (string.IsNullOrWhiteSpace(relativeUrl))
+ {
+ return baseUrl;
+ }
+ if (!relativeUrl.StartsWith("/"))
+ {
+ relativeUrl = "/" + relativeUrl;
+ }
+ return string.Format("{0}{1}", baseUrl, relativeUrl);
+ }
+
+ public static RouteValueDictionary GetRouteValues(RouteValueDictionary routeValues)
+ {
+ return routeValues == null ? new RouteValueDictionary() : new RouteValueDictionary(routeValues);
+ }
+
+ public static RouteValueDictionary MergeRouteValues(string actionName, string controllerName,
+ RouteValueDictionary implicitRouteValues,
+ RouteValueDictionary routeValues,
+ bool includeImplicitMvcValues)
+ {
+ var routeValueDictionary = new RouteValueDictionary();
+ if (includeImplicitMvcValues)
+ {
+ object obj;
+ if (implicitRouteValues != null && implicitRouteValues.TryGetValue("action", out obj))
+ {
+ routeValueDictionary["action"] = obj;
+ }
+ if (implicitRouteValues != null && implicitRouteValues.TryGetValue("controller", out obj))
+ {
+ routeValueDictionary["controller"] = obj;
+ }
+ }
+ if (routeValues != null)
+ {
+ foreach (var keyValuePair in GetRouteValues(routeValues))
+ {
+ routeValueDictionary[keyValuePair.Key] = keyValuePair.Value;
+ }
+ }
+ if (actionName != null)
+ {
+ routeValueDictionary["action"] = actionName;
+ }
+ if (controllerName != null)
+ {
+ routeValueDictionary["controller"] = controllerName;
+ }
+ return routeValueDictionary;
+ }
+ }
}
\ No newline at end of file
From 051654764a3ed1e670fe74f6d79a2751d25b211f Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Wed, 13 Feb 2013 13:36:14 -0700
Subject: [PATCH 36/97] #185 - fixed namespace of app_start files dropped via
nuget.
---
.../AttributeRouting.cs.pp | 11 +++++----
.../AttributeRoutingHttp.cs.pp | 15 +++++++-----
.../AttributeRoutingHttp.vb.pp | 4 ++--
nuget/AttributeRouting/AttributeRouting.cs.pp | 15 +++++++-----
nuget/AttributeRouting/AttributeRouting.vb.pp | 4 ++--
.../AttributeRouting.Tests.Web.csproj | 1 +
.../Controllers/InheritActionsControllers.cs | 24 +++++++++++++++++++
7 files changed, 53 insertions(+), 21 deletions(-)
create mode 100644 src/AttributeRouting.Tests.Web/Controllers/InheritActionsControllers.cs
diff --git a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.cs.pp b/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.cs.pp
index d8a30d8..5d727f7 100644
--- a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.cs.pp
+++ b/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.cs.pp
@@ -1,13 +1,14 @@
using System.Web.Http.SelfHost;
using AttributeRouting.Web.Http.SelfHost;
-namespace $rootnamespace$ {
- public static class AttributeRouting {
-
+namespace $rootnamespace$
+{
+ public static class AttributeRouting
+ {
// Call this static method from a start up class in your applicaton (e.g.Program.cs)
// Pass in the configuration you're using for your self-hosted Web API
- public static void RegisterRoutes(HttpSelfHostConfiguration config) {
-
+ public static void RegisterRoutes(HttpSelfHostConfiguration config)
+ {
// See http://github.com/mccalltd/AttributeRouting/wiki for more options.
// To debug routes locally, you can log to Console.Out (or any other TextWriter) like so:
// config.Routes.Cast().LogTo(Console.Out);
diff --git a/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.cs.pp b/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.cs.pp
index 03bdcfc..30685a9 100644
--- a/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.cs.pp
+++ b/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.cs.pp
@@ -1,19 +1,22 @@
using System.Web.Http;
using AttributeRouting.Web.Http.WebHost;
-[assembly: WebActivator.PreApplicationStartMethod(typeof($rootnamespace$.App_Start.AttributeRoutingHttp), "Start")]
+[assembly: WebActivator.PreApplicationStartMethod(typeof($rootnamespace$.AttributeRoutingHttp), "Start")]
-namespace $rootnamespace$.App_Start {
- public static class AttributeRoutingHttp {
- public static void RegisterRoutes(HttpRouteCollection routes) {
-
+namespace $rootnamespace$
+{
+ public static class AttributeRoutingHttp
+ {
+ public static void RegisterRoutes(HttpRouteCollection routes)
+ {
// See http://github.com/mccalltd/AttributeRouting/wiki for more options.
// To debug routes locally using the built in ASP.NET development server, go to /routes.axd
routes.MapHttpAttributeRoutes();
}
- public static void Start() {
+ public static void Start()
+ {
RegisterRoutes(GlobalConfiguration.Configuration.Routes);
}
}
diff --git a/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.vb.pp b/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.vb.pp
index 676e9fb..43c1cbe 100644
--- a/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.vb.pp
+++ b/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.vb.pp
@@ -1,9 +1,9 @@
Imports System.Web.Http
Imports AttributeRouting.Web.Http.WebHost
-
+
-Namespace $rootnamespace$.App_Start
+Namespace $rootnamespace$
Public Class AttributeRoutingHttp
Public Shared Sub RegisterRoutes(routes As HttpRouteCollection)
diff --git a/nuget/AttributeRouting/AttributeRouting.cs.pp b/nuget/AttributeRouting/AttributeRouting.cs.pp
index 85cda11..56d76cf 100644
--- a/nuget/AttributeRouting/AttributeRouting.cs.pp
+++ b/nuget/AttributeRouting/AttributeRouting.cs.pp
@@ -1,19 +1,22 @@
using System.Web.Routing;
using AttributeRouting.Web.Mvc;
-[assembly: WebActivator.PreApplicationStartMethod(typeof($rootnamespace$.App_Start.AttributeRouting), "Start")]
+[assembly: WebActivator.PreApplicationStartMethod(typeof($rootnamespace$.AttributeRouting), "Start")]
-namespace $rootnamespace$.App_Start {
- public static class AttributeRouting {
- public static void RegisterRoutes(RouteCollection routes) {
-
+namespace $rootnamespace$
+{
+ public static class AttributeRouting
+ {
+ public static void RegisterRoutes(RouteCollection routes)
+ {
// See http://github.com/mccalltd/AttributeRouting/wiki for more options.
// To debug routes locally using the built in ASP.NET development server, go to /routes.axd
routes.MapAttributeRoutes();
}
- public static void Start() {
+ public static void Start()
+ {
RegisterRoutes(RouteTable.Routes);
}
}
diff --git a/nuget/AttributeRouting/AttributeRouting.vb.pp b/nuget/AttributeRouting/AttributeRouting.vb.pp
index 160e322..5c44b81 100644
--- a/nuget/AttributeRouting/AttributeRouting.vb.pp
+++ b/nuget/AttributeRouting/AttributeRouting.vb.pp
@@ -1,9 +1,9 @@
Imports System.Web.Routing
Imports AttributeRouting.Web.Mvc
-
+
-Namespace $rootnamespace$.App_Start
+Namespace $rootnamespace$
Public Class AttributeRouting
Public Shared Sub RegisterRoutes(routes As RouteCollection)
diff --git a/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj b/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj
index 52d0ae9..48fb7e7 100644
--- a/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj
+++ b/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj
@@ -126,6 +126,7 @@
+
diff --git a/src/AttributeRouting.Tests.Web/Controllers/InheritActionsControllers.cs b/src/AttributeRouting.Tests.Web/Controllers/InheritActionsControllers.cs
new file mode 100644
index 0000000..f57c106
--- /dev/null
+++ b/src/AttributeRouting.Tests.Web/Controllers/InheritActionsControllers.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+using AttributeRouting.Web.Mvc;
+
+namespace AttributeRouting.Tests.Web.Controllers
+{
+ public abstract class InheritActionsControllerBase : Controller
+ {
+ [GET("Base-Method")]
+ public string Index()
+ {
+ return "Base Index";
+ }
+ }
+
+ [RoutePrefix("Inherit/Derived")]
+ public class InheritActionsDerivedController : InheritActionsControllerBase
+ {
+
+ }
+}
From 5b30293bc580441ae8c65f5d51e9088ae31e703c Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Wed, 13 Feb 2013 13:55:25 -0700
Subject: [PATCH 37/97] #183 - now allowing route conventions to be applied on
the base controller.
---
.../Features/RouteConventions.feature | 11 ++----
.../Features/RouteConventions.feature.cs | 20 ++++------
.../Subjects/RouteConventionsControllers.cs | 39 ++++++++++---------
.../Framework/RouteBuilder.cs | 4 +-
.../Framework/RouteReflector.cs | 2 +-
5 files changed, 35 insertions(+), 41 deletions(-)
diff --git a/src/AttributeRouting.Specs/Features/RouteConventions.feature b/src/AttributeRouting.Specs/Features/RouteConventions.feature
index 3714479..5e422f7 100644
--- a/src/AttributeRouting.Specs/Features/RouteConventions.feature
+++ b/src/AttributeRouting.Specs/Features/RouteConventions.feature
@@ -97,10 +97,7 @@ Scenario: Generating routes using the DefaultHttpRouteConvention on actions with
Then the 1st route url is "DefaultHttpRouteConventionWithExplicitOrderedRoute/Primary"
And the 2nd route url is "DefaultHttpRouteConventionWithExplicitOrderedRoute"
-Scenario: Generating routes using the conventions that define areas on controllers
- Given I have registered the routes for the AreaRouteConventionController
- When I fetch the routes for the AreaRouteConvention controller's Index action
- Then the route url is "Subjects/Index"
- And the default for "controller" is "AreaRouteConvention"
- And the default for "action" is "Index"
- And the route area is "Subjects"
+Scenario: Generating routes using the conventions defined on base controllers
+ Given I have registered the routes for the DerivedFakeConventionController
+ When I fetch the routes for the DerivedFakeConventionController's Index action
+ Then the route url is "Yowza/Index"
diff --git a/src/AttributeRouting.Specs/Features/RouteConventions.feature.cs b/src/AttributeRouting.Specs/Features/RouteConventions.feature.cs
index 101219c..354e53e 100644
--- a/src/AttributeRouting.Specs/Features/RouteConventions.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteConventions.feature.cs
@@ -3,7 +3,7 @@
// This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:1.9.0.77
// SpecFlow Generator Version:1.9.0.0
-// Runtime Version:4.0.30319.18010
+// Runtime Version:4.0.30319.18034
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -284,24 +284,18 @@ public virtual void GeneratingRoutesUsingTheDefaultHttpRouteConventionOnActionsW
}
[NUnit.Framework.TestAttribute()]
- [NUnit.Framework.DescriptionAttribute("Generating routes using the conventions that define areas on controllers")]
- public virtual void GeneratingRoutesUsingTheConventionsThatDefineAreasOnControllers()
+ [NUnit.Framework.DescriptionAttribute("Generating routes using the conventions defined on base controllers")]
+ public virtual void GeneratingRoutesUsingTheConventionsDefinedOnBaseControllers()
{
- TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes using the conventions that define areas on controllers", ((string[])(null)));
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Generating routes using the conventions defined on base controllers", ((string[])(null)));
#line 100
this.ScenarioSetup(scenarioInfo);
#line 101
- testRunner.Given("I have registered the routes for the AreaRouteConventionController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+ testRunner.Given("I have registered the routes for the DerivedFakeConventionController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 102
- testRunner.When("I fetch the routes for the AreaRouteConvention controller\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+ testRunner.When("I fetch the routes for the DerivedFakeConventionController\'s Index action", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 103
- testRunner.Then("the route url is \"Subjects/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 104
- testRunner.And("the default for \"controller\" is \"AreaRouteConvention\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 105
- testRunner.And("the default for \"action\" is \"Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 106
- testRunner.And("the route area is \"Subjects\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+ testRunner.Then("the route url is \"Yowza/Index\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Subjects/RouteConventionsControllers.cs b/src/AttributeRouting.Specs/Subjects/RouteConventionsControllers.cs
index b71ffb7..e79e646 100644
--- a/src/AttributeRouting.Specs/Subjects/RouteConventionsControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/RouteConventionsControllers.cs
@@ -122,28 +122,31 @@ public ActionResult Index()
{
return Content("");
}
- }
-
- [AreaRouteConvention]
- public class AreaRouteConventionController : Controller
- {
- public ActionResult Index()
- {
- return Content("");
- }
- }
-
- public class AreaRouteConventionAttribute : RouteConventionAttributeBase
+ }
+
+ public class FakeConventionAttribute : RouteConventionAttributeBase
{
public override IEnumerable GetRouteAttributes(MethodInfo actionMethod)
{
yield return new GETAttribute(actionMethod.Name);
- }
-
- public override RouteAreaAttribute GetDefaultRouteArea(Type controllerType)
+ }
+
+ public override IEnumerable GetDefaultRoutePrefixes(Type controllerType)
+ {
+ yield return new RoutePrefixAttribute("Yowza");
+ }
+ }
+
+ [FakeConvention]
+ public abstract class FakeConventionControllerBase : Controller
+ {
+ }
+
+ public class DerivedFakeConventionController : FakeConventionControllerBase
+ {
+ public ActionResult Index()
{
- var areaName = controllerType.Namespace.ValueOr("").Split('.').Last();
- return new RouteAreaAttribute(areaName);
+ return Content("");
}
- }
+ }
}
\ No newline at end of file
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index ee8a6d8..bf3367c 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -43,7 +43,7 @@ where s.Subdomain.HasValue()
foreach (var routeSpec in routeSpecs)
{
- foreach (var route in Build(routeSpec))
+ foreach (var route in BuildRoutes(routeSpec))
{
route.MappedSubdomains = mappedSubdomains;
yield return route;
@@ -51,7 +51,7 @@ where s.Subdomain.HasValue()
}
}
- private IEnumerable Build(RouteSpecification routeSpec)
+ private IEnumerable BuildRoutes(RouteSpecification routeSpec)
{
var defaults = CreateRouteDefaults(routeSpec);
var constraints = CreateRouteConstraints(routeSpec);
diff --git a/src/AttributeRouting/Framework/RouteReflector.cs b/src/AttributeRouting/Framework/RouteReflector.cs
index a0a86b9..ae36f0e 100644
--- a/src/AttributeRouting/Framework/RouteReflector.cs
+++ b/src/AttributeRouting/Framework/RouteReflector.cs
@@ -56,7 +56,7 @@ private IEnumerable BuildRouteSpecifications(IEnumerable(false);
+ var convention = controllerType.GetCustomAttribute(true);
var routeAreaAttribute = GetRouteAreaAttribute(controllerType, convention);
// For each action method on the controller:
From 52d0708005600055e91e3f7868ba46df24260a50 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Wed, 13 Feb 2013 14:27:26 -0700
Subject: [PATCH 38/97] #182 - inlined scripts used by the routes.axd to
eliminate reliance on CDN.
---
src/AttributeRouting.Web.Mvc/Configuration.cs | 4 +-
.../AttributeRouting.Web.csproj | 4 +
.../Logging/LogRoutes.html | 9 +-
.../Logging/LogRoutesHandler.cs | 44 +++++++++-
.../Logging/LoggingExtensions.cs | 15 ++--
.../Logging/jquery-1.9.1.min.js | 5 ++
.../Logging/knockout-2.2.1.js | 85 +++++++++++++++++++
7 files changed, 148 insertions(+), 18 deletions(-)
create mode 100644 src/AttributeRouting.Web/Logging/jquery-1.9.1.min.js
create mode 100644 src/AttributeRouting.Web/Logging/knockout-2.2.1.js
diff --git a/src/AttributeRouting.Web.Mvc/Configuration.cs b/src/AttributeRouting.Web.Mvc/Configuration.cs
index 3ac3913..33bf721 100644
--- a/src/AttributeRouting.Web.Mvc/Configuration.cs
+++ b/src/AttributeRouting.Web.Mvc/Configuration.cs
@@ -21,8 +21,8 @@ public Configuration()
RegisterDefaultInlineRouteConstraints(typeof(RegexRouteConstraint).Assembly);
}
- public Func RouteHandlerFactory { get; set; }
-
+ public Func RouteHandlerFactory { get; set; }
+
///
/// The controller type applicable to this context.
///
diff --git a/src/AttributeRouting.Web/AttributeRouting.Web.csproj b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
index 423f01d..93c34cf 100644
--- a/src/AttributeRouting.Web/AttributeRouting.Web.csproj
+++ b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
@@ -88,6 +88,10 @@
+
+
+
+
+ -->
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs b/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
index 1f6cf70..c19849d 100644
--- a/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
+++ b/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
@@ -1,8 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Web;
-using System.Web.Routing;
-using AttributeRouting.Constraints;
+using System.Web.Routing;
using AttributeRouting.Framework;
using AttributeRouting.Helpers;
diff --git a/src/AttributeRouting.Web/AttributeRouting.Web.csproj b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
index 93c34cf..8d8e811 100644
--- a/src/AttributeRouting.Web/AttributeRouting.Web.csproj
+++ b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
@@ -62,6 +62,7 @@
+
@@ -76,12 +77,6 @@
-
-
- {871A79CF-C705-4C6B-8938-F9AA1E02AEA4}
- AttributeRouting
-
-
@@ -92,6 +87,12 @@
+
+
+ {871a79cf-c705-4c6b-8938-f9aa1e02aea4}
+ AttributeRouting
+
+
+ -->
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs b/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
index d61f1da..6756823 100644
--- a/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/HttpRouteCollectionExtensions.cs
@@ -1,61 +1,70 @@
-using System;
-using System.Linq;
-using System.Reflection;
-using System.Web.Http;
-using System.Web.Routing;
-using AttributeRouting.Framework;
-using AttributeRouting.Web.Http.Framework;
-
-namespace AttributeRouting.Web.Http.WebHost
-{
- ///
- /// Extensions to the System.Web.Http.HttpRouteCollection
- ///
- public static class HttpRouteCollectionExtensions
- {
- ///
- /// Scans the calling assembly for all routes defined with AttributeRouting attributes,
- /// using the default conventions.
- ///
- public static void MapHttpAttributeRoutes(this HttpRouteCollection routes)
- {
- var configuration = new HttpWebConfiguration();
- configuration.AddRoutesFromAssembly(Assembly.GetCallingAssembly());
-
- routes.MapHttpAttributeRoutesInternal(configuration);
- }
-
- ///
- /// Scans the specified assemblies for all routes defined with AttributeRouting attributes,
- /// and applies configuration options against the routes found.
- ///
- ///
- /// The initialization action that builds the configuration object
- public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, Action configurationAction)
- {
- var configuration = new HttpWebConfiguration();
- configurationAction.Invoke(configuration);
-
- routes.MapHttpAttributeRoutesInternal(configuration);
- }
-
- ///
- /// Scans the specified assemblies for all routes defined with AttributeRouting attributes,
- /// and applies configuration options against the routes found.
- ///
- ///
- /// The configuration object
- public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, HttpWebConfiguration configuration)
- {
- routes.MapHttpAttributeRoutesInternal(configuration);
- }
-
- private static void MapHttpAttributeRoutesInternal(this HttpRouteCollection routes, HttpWebConfiguration configuration)
- {
- new RouteBuilder(configuration).BuildAllRoutes()
- .Cast()
- .ToList()
- .ForEach(r => routes.Add(r.RouteName, r));
- }
- }
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Web.Http;
+using System.Web.Routing;
+using AttributeRouting.Framework;
+using AttributeRouting.Web.Http.Framework;
+
+namespace AttributeRouting.Web.Http.WebHost
+{
+ ///
+ /// Extensions to the System.Web.Http.HttpRouteCollection
+ ///
+ public static class HttpRouteCollectionExtensions
+ {
+ ///
+ /// Scans the calling assembly for all routes defined with AttributeRouting attributes,
+ /// using the default conventions.
+ ///
+ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes)
+ {
+ var configuration = new HttpWebConfiguration();
+ configuration.AddRoutesFromAssembly(Assembly.GetCallingAssembly());
+
+ routes.MapHttpAttributeRoutesInternal(configuration);
+ }
+
+ ///
+ /// Scans the specified assemblies for all routes defined with AttributeRouting attributes,
+ /// and applies configuration options against the routes found.
+ ///
+ ///
+ /// The initialization action that builds the configuration object
+ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, Action configurationAction)
+ {
+ var configuration = new HttpWebConfiguration();
+ configurationAction.Invoke(configuration);
+
+ if (configuration.InMemory)
+ {
+ var newConfig = new HttpWebConfiguration(inMemory: true);
+ configurationAction.Invoke(newConfig);
+ routes.MapHttpAttributeRoutesInternal(newConfig);
+ }
+ else
+ {
+ routes.MapHttpAttributeRoutesInternal(configuration);
+ }
+ }
+
+ ///
+ /// Scans the specified assemblies for all routes defined with AttributeRouting attributes,
+ /// and applies configuration options against the routes found.
+ ///
+ ///
+ /// The configuration object
+ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, HttpWebConfiguration configuration)
+ {
+ routes.MapHttpAttributeRoutesInternal(configuration);
+ }
+
+ private static void MapHttpAttributeRoutesInternal(this HttpRouteCollection routes, HttpWebConfiguration configuration)
+ {
+ new RouteBuilder(configuration).BuildAllRoutes()
+ .Cast()
+ .ToList()
+ .ForEach(r => routes.Add(r.RouteName, r));
+ }
+ }
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http.WebHost/HttpWebConfiguration.cs b/src/AttributeRouting.Web.Http.WebHost/HttpWebConfiguration.cs
index 6f595b3..5b67417 100644
--- a/src/AttributeRouting.Web.Http.WebHost/HttpWebConfiguration.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/HttpWebConfiguration.cs
@@ -6,14 +6,22 @@ namespace AttributeRouting.Web.Http.WebHost
{
public class HttpWebConfiguration : HttpConfigurationBase
{
- public HttpWebConfiguration()
+ public HttpWebConfiguration(bool inMemory = false)
{
- AttributeRouteFactory = new AttributeRouteFactory(this);
- ParameterFactory = new RouteParameterFactory();
- RouteConstraintFactory = new RouteConstraintFactory(this);
+ if (inMemory)
+ {
+ Init();
+ }
+ else
+ {
+ AttributeRouteFactory = new AttributeRouteFactory(this);
+ ParameterFactory = new RouteParameterFactory();
+ RouteConstraintFactory = new RouteConstraintFactory(this);
- RouteHandlerFactory = () => null;
- RegisterDefaultInlineRouteConstraints(typeof(Web.Constraints.RegexRouteConstraint).Assembly);
+ RouteHandlerFactory = () => null;
+ RegisterDefaultInlineRouteConstraints(
+ typeof(Web.Constraints.RegexRouteConstraint).Assembly);
+ }
}
///
diff --git a/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj b/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
index 3434b26..a24caf7 100644
--- a/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
+++ b/src/AttributeRouting.Web.Http/AttributeRouting.Web.Http.csproj
@@ -1,117 +1,123 @@
-
-
-
- Debug
- AnyCPU
- 8.0.30703
- 2.0
- {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}
- Library
- Properties
- AttributeRouting.Web.Http
- AttributeRouting.Web.Http
- v4.0
- 512
- ..\
- true
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
- bin\Release\AttributeRouting.Web.Http.xml
- 1591, 1587
-
-
- true
-
-
- AttributeRouting.snk
-
-
-
- False
- ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll
-
-
-
-
- ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll
-
-
- ..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll
-
-
- ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll
-
-
-
- ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll
-
-
-
-
-
-
-
-
-
- SharedAssemblyInfo.cs
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {871a79cf-c705-4c6b-8938-f9aa1e02aea4}
- AttributeRouting
-
-
-
-
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}
+ Library
+ Properties
+ AttributeRouting.Web.Http
+ AttributeRouting.Web.Http
+ v4.0
+ 512
+ ..\
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ bin\Release\AttributeRouting.Web.Http.xml
+ 1591, 1587
+
+
+ true
+
+
+ AttributeRouting.snk
+
+
+
+ False
+ ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll
+
+
+
+
+ ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll
+
+
+ ..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll
+
+
+ ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll
+
+
+
+ ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll
+
+
+
+
+
+
+
+
+
+ SharedAssemblyInfo.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {871a79cf-c705-4c6b-8938-f9aa1e02aea4}
+ AttributeRouting
+
+
+
+
+ -->
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Constraints/InboundHttpMethodConstraint.cs b/src/AttributeRouting.Web.Http/Constraints/InboundHttpMethodConstraint.cs
similarity index 92%
rename from src/AttributeRouting.Web.Http.SelfHost/Constraints/InboundHttpMethodConstraint.cs
rename to src/AttributeRouting.Web.Http/Constraints/InboundHttpMethodConstraint.cs
index e4148ca..dd88006 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Constraints/InboundHttpMethodConstraint.cs
+++ b/src/AttributeRouting.Web.Http/Constraints/InboundHttpMethodConstraint.cs
@@ -5,7 +5,7 @@
using System.Web.Http.Routing;
using AttributeRouting.Constraints;
-namespace AttributeRouting.Web.Http.SelfHost.Constraints
+namespace AttributeRouting.Web.Http.Constraints
{
public class InboundHttpMethodConstraint : HttpMethodConstraint, IInboundHttpMethodConstraint
{
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Constraints/OptionalRouteConstraint.cs b/src/AttributeRouting.Web.Http/Constraints/OptionalRouteConstraint.cs
similarity index 95%
rename from src/AttributeRouting.Web.Http.SelfHost/Constraints/OptionalRouteConstraint.cs
rename to src/AttributeRouting.Web.Http/Constraints/OptionalRouteConstraint.cs
index f441268..e2a5500 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Constraints/OptionalRouteConstraint.cs
+++ b/src/AttributeRouting.Web.Http/Constraints/OptionalRouteConstraint.cs
@@ -5,7 +5,7 @@
using AttributeRouting.Constraints;
using AttributeRouting.Helpers;
-namespace AttributeRouting.Web.Http.SelfHost.Constraints
+namespace AttributeRouting.Web.Http.Constraints
{
public class OptionalRouteConstraint : IOptionalRouteConstraint, IHttpRouteConstraint
{
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Framework/AttributeRouteFactory.cs b/src/AttributeRouting.Web.Http/Framework/AttributeRouteFactory.cs
similarity index 78%
rename from src/AttributeRouting.Web.Http.SelfHost/Framework/AttributeRouteFactory.cs
rename to src/AttributeRouting.Web.Http/Framework/AttributeRouteFactory.cs
index b18ae51..b1c6f68 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Framework/AttributeRouteFactory.cs
+++ b/src/AttributeRouting.Web.Http/Framework/AttributeRouteFactory.cs
@@ -1,15 +1,14 @@
using System.Collections.Generic;
using System.Web.Http.Routing;
using AttributeRouting.Framework;
-using AttributeRouting.Web.Http.Framework;
-namespace AttributeRouting.Web.Http.SelfHost.Framework
+namespace AttributeRouting.Web.Http.Framework
{
internal class AttributeRouteFactory : IAttributeRouteFactory
{
- private readonly HttpConfiguration _configuration;
+ private readonly HttpConfigurationBase _configuration;
- public AttributeRouteFactory(HttpConfiguration configuration)
+ public AttributeRouteFactory(HttpConfigurationBase configuration)
{
_configuration = configuration;
}
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Framework/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Http/Framework/RouteConstraintFactory.cs
similarity index 90%
rename from src/AttributeRouting.Web.Http.SelfHost/Framework/RouteConstraintFactory.cs
rename to src/AttributeRouting.Web.Http/Framework/RouteConstraintFactory.cs
index 95494cc..9a1ff37 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Framework/RouteConstraintFactory.cs
+++ b/src/AttributeRouting.Web.Http/Framework/RouteConstraintFactory.cs
@@ -7,15 +7,14 @@
using AttributeRouting.Framework;
using AttributeRouting.Helpers;
using AttributeRouting.Web.Http.Constraints;
-using AttributeRouting.Web.Http.SelfHost.Constraints;
-namespace AttributeRouting.Web.Http.SelfHost.Framework
+namespace AttributeRouting.Web.Http.Framework
{
public class RouteConstraintFactory : IRouteConstraintFactory
{
- private readonly HttpConfiguration _configuration;
+ private readonly HttpConfigurationBase _configuration;
- public RouteConstraintFactory(HttpConfiguration configuration)
+ public RouteConstraintFactory(HttpConfigurationBase configuration)
{
_configuration = configuration;
}
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Framework/RouteParameterFactory.cs b/src/AttributeRouting.Web.Http/Framework/RouteParameterFactory.cs
similarity index 80%
rename from src/AttributeRouting.Web.Http.SelfHost/Framework/RouteParameterFactory.cs
rename to src/AttributeRouting.Web.Http/Framework/RouteParameterFactory.cs
index b4676d7..c615801 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Framework/RouteParameterFactory.cs
+++ b/src/AttributeRouting.Web.Http/Framework/RouteParameterFactory.cs
@@ -1,7 +1,7 @@
using System.Web.Http;
using AttributeRouting.Framework;
-namespace AttributeRouting.Web.Http.SelfHost.Framework
+namespace AttributeRouting.Web.Http.Framework
{
internal class RouteParameterFactory : IParameterFactory
{
diff --git a/src/AttributeRouting.Web.Http.SelfHost/HttpConfiguration.cs b/src/AttributeRouting.Web.Http/HttpConfiguration.cs
similarity index 55%
rename from src/AttributeRouting.Web.Http.SelfHost/HttpConfiguration.cs
rename to src/AttributeRouting.Web.Http/HttpConfiguration.cs
index 366db2e..adea2f5 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/HttpConfiguration.cs
+++ b/src/AttributeRouting.Web.Http/HttpConfiguration.cs
@@ -1,23 +1,15 @@
using System.Web.Http.Routing;
using AttributeRouting.Framework;
using AttributeRouting.Web.Http.Constraints;
-using AttributeRouting.Web.Http.SelfHost.Framework;
+using AttributeRouting.Web.Http.Framework;
-namespace AttributeRouting.Web.Http.SelfHost
+namespace AttributeRouting.Web.Http
{
public class HttpConfiguration : HttpConfigurationBase
{
public HttpConfiguration()
{
- AttributeRouteFactory = new AttributeRouteFactory(this);
- RouteConstraintFactory = new RouteConstraintFactory(this);
- ParameterFactory = new RouteParameterFactory();
-
- RegisterDefaultInlineRouteConstraints(typeof(RegexRouteConstraint).Assembly);
-
- // Must turn on AutoGenerateRouteNames and use the Unique RouteNameBuilder for this to work out-of-the-box.
- AutoGenerateRouteNames = true;
- RouteNameBuilder = RouteNameBuilders.Unique;
+ Init();
}
///
diff --git a/src/AttributeRouting.Web.Http/HttpConfigurationBase.cs b/src/AttributeRouting.Web.Http/HttpConfigurationBase.cs
index 451132b..b03e095 100644
--- a/src/AttributeRouting.Web.Http/HttpConfigurationBase.cs
+++ b/src/AttributeRouting.Web.Http/HttpConfigurationBase.cs
@@ -3,6 +3,9 @@
using System.Threading;
using System.Web.Http.Controllers;
using System.Web.Http.Routing;
+using AttributeRouting.Framework;
+using AttributeRouting.Web.Http.Constraints;
+using AttributeRouting.Web.Http.Framework;
namespace AttributeRouting.Web.Http
{
@@ -13,6 +16,24 @@ protected HttpConfigurationBase()
CurrentUICultureResolver = (ctx, data) => Thread.CurrentThread.CurrentUICulture.Name;
}
+ public void Init()
+ {
+ AttributeRouteFactory = new AttributeRouteFactory(this);
+ RouteConstraintFactory = new RouteConstraintFactory(this);
+ ParameterFactory = new RouteParameterFactory();
+
+ RegisterDefaultInlineRouteConstraints(typeof(RegexRouteConstraint).Assembly);
+
+ // Must turn on AutoGenerateRouteNames and use the Unique RouteNameBuilder for this to work out-of-the-box.
+ AutoGenerateRouteNames = true;
+ RouteNameBuilder = RouteNameBuilders.Unique;
+ }
+
+ ///
+ /// Defines whether the Web API pipeline will run in memory or not.
+ ///
+ public bool InMemory { get; set; }
+
///
/// The message handler that will be the recipient of the request.
///
From c072da88eaa48f26a92fc134f1387c9b206a6b9b Mon Sep 17 00:00:00 2001
From: Filip W
Date: Thu, 21 Feb 2013 20:57:19 -0500
Subject: [PATCH 63/97] added tests
tests for issue 191
---
.../Tests/BugFixTests.cs | 312 ++++++++++--------
1 file changed, 177 insertions(+), 135 deletions(-)
diff --git a/src/AttributeRouting.Specs/Tests/BugFixTests.cs b/src/AttributeRouting.Specs/Tests/BugFixTests.cs
index 6e5f728..e1579e1 100644
--- a/src/AttributeRouting.Specs/Tests/BugFixTests.cs
+++ b/src/AttributeRouting.Specs/Tests/BugFixTests.cs
@@ -1,135 +1,177 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Web.Http;
-using System.Web.Mvc;
-using System.Web.Routing;
-using AttributeRouting.Framework.Localization;
-using AttributeRouting.Specs.Subjects;
-using AttributeRouting.Specs.Subjects.Http;
-using AttributeRouting.Web.Http.WebHost;
-using AttributeRouting.Web.Logging;
-using AttributeRouting.Web.Mvc;
-using MvcContrib.TestHelper;
-using NUnit.Framework;
-
-namespace AttributeRouting.Specs.Tests
-{
- public class BugFixTests
- {
- [Test]
- public void Issue161_Querystring_param_constraints_mucks_up_url_generation()
- {
- // re: issue #161
-
- var routes = RouteTable.Routes;
- routes.Clear();
- routes.MapAttributeRoutes(config => config.AddRoutesFromController());
-
- var urlHelper = new UrlHelper(MockBuilder.BuildRequestContext());
- var routeValues = new { area = "Cms", culture = "en", p = 1 };
- var expectedUrl = urlHelper.Action("Index", "Issue161Test", routeValues);
-
- Assert.That(expectedUrl, Is.EqualTo("/en/Cms/Content/Items?p=1"));
- }
-
- [Test]
- public void Issue120_OData_style_http_url_bonks()
- {
- // re: issue #120
-
- var httpRoutes = GlobalConfiguration.Configuration.Routes;
- httpRoutes.Clear();
- httpRoutes.MapHttpAttributeRoutes(config => config.AddRoutesFromController());
-
- // Just make sure we don't get an exception
- Assert.That(httpRoutes.Count, Is.EqualTo(1));
- }
-
- [Test]
- public void Issue102_Generating_two_routes_for_api_get_requests()
- {
- // re: issue #102
-
- var httpRoutes = GlobalConfiguration.Configuration.Routes;
- httpRoutes.Clear();
- httpRoutes.MapHttpAttributeRoutes(config => config.AddRoutesFromController());
-
- var routes = RouteTable.Routes.Cast().ToList();
-
- Assert.That(routes.Count, Is.EqualTo(2));
- }
-
- [Test]
- public void Issue25_Ensure_that_incompletely_mocked_request_context_does_not_generate_error_in_determining_http_method()
- {
- // re: issue #25
-
- RouteTable.Routes.Clear();
- RouteTable.Routes.MapAttributeRoutes(config => config.AddRoutesFromController());
-
- "~/Index"
- .ShouldMapTo(
- x => x.Index());
- }
-
- [Test]
- public void Issue43_Ensure_that_routes_with_optional_url_params_are_correctly_matched()
- {
- // re: issue #43
-
- RouteTable.Routes.Clear();
- RouteTable.Routes.MapAttributeRoutes(config => config.AddRoutesFromController());
-
- RouteTable.Routes.Cast().LogTo(Console.Out);
-
- "~/BugFixes/Gallery/_CenterImage"
- .ShouldMapTo(
- x => x.Issue43_OptionalParamsAreMucky(null, null, null, null));
- }
-
- [Test]
- public void Issue53_Ensure_that_inbound_routing_works_when_contraining_by_culture()
- {
- // re: issue #53
-
- var translations = new FluentTranslationProvider();
- translations.AddTranslations().ForController().RouteUrl(x => x.Index(), new Dictionary { { "pt", "Inicio" } });
- translations.AddTranslations().ForController().RouteUrl(x => x.Index(), new Dictionary { { "en", "Home" } });
-
- RouteTable.Routes.Clear();
- RouteTable.Routes.MapAttributeRoutes(config =>
- {
- config.AddRoutesFromController();
- config.AddTranslationProvider(translations);
- config.ConstrainTranslatedRoutesByCurrentUICulture = true;
- config.CurrentUICultureResolver = (httpContext, routeData) =>
- {
- return (string)routeData.Values["culture"]
- ?? Thread.CurrentThread.CurrentUICulture.Name;
- };
- });
-
- RouteTable.Routes.Cast().LogTo(Console.Out);
-
- "~/en/cms/home".ShouldMapTo(x => x.Index());
- Assert.That("~/en/cms/inicio".Route(), Is.Null);
- Assert.That("~/pt/cms/home".Route(), Is.Null);
- "~/pt/cms/inicio".ShouldMapTo(x => x.Index());
- }
-
- [Test]
- public void Issue84_Ensure_that_async_controller_action_can_be_mapped()
- {
- // re: issue #84
- RouteTable.Routes.Clear();
- RouteTable.Routes.MapAttributeRoutes(config => config.AddRoutesFromController());
-
- "~/WithAsync/Synchronous".ShouldMapTo(x => x.Test1());
- var asyncRouteData = "~/WithAsync/NotSynchronous".Route();
- asyncRouteData.Values["controller"].ShouldEqual("AsyncAction", "Asynchronous route does not map to the AsyncActionController.");
- asyncRouteData.Values["action"].ShouldEqual("Test2", "Asynchronous route does not map to the correct action method.");
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Web.Http;
+using System.Web.Mvc;
+using System.Web.Routing;
+using AttributeRouting.Framework.Localization;
+using AttributeRouting.Specs.Subjects;
+using AttributeRouting.Specs.Subjects.Http;
+using AttributeRouting.Web.Http.Constraints;
+using AttributeRouting.Web.Http.WebHost;
+using AttributeRouting.Web.Logging;
+using AttributeRouting.Web.Mvc;
+using MvcContrib.TestHelper;
+using NUnit.Framework;
+
+namespace AttributeRouting.Specs.Tests
+{
+ public class BugFixTests
+ {
+ [Test]
+ public void Issue161_Querystring_param_constraints_mucks_up_url_generation()
+ {
+ // re: issue #161
+
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config => config.AddRoutesFromController());
+
+ var urlHelper = new UrlHelper(MockBuilder.BuildRequestContext());
+ var routeValues = new { area = "Cms", culture = "en", p = 1 };
+ var expectedUrl = urlHelper.Action("Index", "Issue161Test", routeValues);
+
+ Assert.That(expectedUrl, Is.EqualTo("/en/Cms/Content/Items?p=1"));
+ }
+
+ [Test]
+ public void Issue120_OData_style_http_url_bonks()
+ {
+ // re: issue #120
+
+ var httpRoutes = GlobalConfiguration.Configuration.Routes;
+ httpRoutes.Clear();
+ httpRoutes.MapHttpAttributeRoutes(config => config.AddRoutesFromController());
+
+ // Just make sure we don't get an exception
+ Assert.That(httpRoutes.Count, Is.EqualTo(1));
+ }
+
+ [Test]
+ public void Issue102_Generating_two_routes_for_api_get_requests()
+ {
+ // re: issue #102
+
+ var httpRoutes = GlobalConfiguration.Configuration.Routes;
+ httpRoutes.Clear();
+ httpRoutes.MapHttpAttributeRoutes(config => config.AddRoutesFromController());
+
+ var routes = RouteTable.Routes.Cast().ToList();
+
+ Assert.That(routes.Count, Is.EqualTo(2));
+ }
+
+ [Test]
+ public void Issue25_Ensure_that_incompletely_mocked_request_context_does_not_generate_error_in_determining_http_method()
+ {
+ // re: issue #25
+
+ RouteTable.Routes.Clear();
+ RouteTable.Routes.MapAttributeRoutes(config => config.AddRoutesFromController());
+
+ "~/Index"
+ .ShouldMapTo(
+ x => x.Index());
+ }
+
+ [Test]
+ public void Issue43_Ensure_that_routes_with_optional_url_params_are_correctly_matched()
+ {
+ // re: issue #43
+
+ RouteTable.Routes.Clear();
+ RouteTable.Routes.MapAttributeRoutes(config => config.AddRoutesFromController());
+
+ RouteTable.Routes.Cast().LogTo(Console.Out);
+
+ "~/BugFixes/Gallery/_CenterImage"
+ .ShouldMapTo(
+ x => x.Issue43_OptionalParamsAreMucky(null, null, null, null));
+ }
+
+ [Test]
+ public void Issue53_Ensure_that_inbound_routing_works_when_contraining_by_culture()
+ {
+ // re: issue #53
+
+ var translations = new FluentTranslationProvider();
+ translations.AddTranslations().ForController().RouteUrl(x => x.Index(), new Dictionary { { "pt", "Inicio" } });
+ translations.AddTranslations().ForController().RouteUrl(x => x.Index(), new Dictionary { { "en", "Home" } });
+
+ RouteTable.Routes.Clear();
+ RouteTable.Routes.MapAttributeRoutes(config =>
+ {
+ config.AddRoutesFromController();
+ config.AddTranslationProvider(translations);
+ config.ConstrainTranslatedRoutesByCurrentUICulture = true;
+ config.CurrentUICultureResolver = (httpContext, routeData) =>
+ {
+ return (string)routeData.Values["culture"]
+ ?? Thread.CurrentThread.CurrentUICulture.Name;
+ };
+ });
+
+ RouteTable.Routes.Cast().LogTo(Console.Out);
+
+ "~/en/cms/home".ShouldMapTo(x => x.Index());
+ Assert.That("~/en/cms/inicio".Route(), Is.Null);
+ Assert.That("~/pt/cms/home".Route(), Is.Null);
+ "~/pt/cms/inicio".ShouldMapTo(x => x.Index());
+ }
+
+ [Test]
+ public void Issue84_Ensure_that_async_controller_action_can_be_mapped()
+ {
+ // re: issue #84
+ RouteTable.Routes.Clear();
+ RouteTable.Routes.MapAttributeRoutes(config => config.AddRoutesFromController());
+
+ "~/WithAsync/Synchronous".ShouldMapTo(x => x.Test1());
+ var asyncRouteData = "~/WithAsync/NotSynchronous".Route();
+ asyncRouteData.Values["controller"].ShouldEqual("AsyncAction", "Asynchronous route does not map to the AsyncActionController.");
+ asyncRouteData.Values["action"].ShouldEqual("Test2", "Asynchronous route does not map to the correct action method.");
+ }
+
+ [Test]
+ public void Issue191_in_memory_config_initializes_routes_with_general_http_constraints()
+ {
+ var inMemoryConfig = new HttpConfiguration();
+
+ inMemoryConfig.Routes.MapHttpAttributeRoutes(x =>
+ {
+ x.InMemory = true;
+ x.AddRoutesFromController();
+ });
+
+ Assert.AreEqual(6, inMemoryConfig.Routes.Count);
+ Assert.True(inMemoryConfig.Routes.All(x => x.Constraints.All(c => c.Value.GetType() == typeof(InboundHttpMethodConstraint))));
+ }
+
+ [Test]
+ public void Issue191_default_web_config_initializes_routes_with_web_http_constraints()
+ {
+ var inMemoryConfig = GlobalConfiguration.Configuration;
+ inMemoryConfig.Routes.Clear();
+
+ inMemoryConfig.Routes.MapHttpAttributeRoutes(x => x.AddRoutesFromController());
+
+ Assert.AreEqual(6, inMemoryConfig.Routes.Count);
+ Assert.True(inMemoryConfig.Routes.All(x => x.Constraints.All(c => c.Value.GetType() == typeof(Web.Http.WebHost.Constraints.InboundHttpMethodConstraint))));
+ }
+
+ [Test]
+ public void Issue191_in_memory_web_config_inits_general_http_constraint_factory()
+ {
+ var inMemoryConfig = new HttpWebConfiguration(inMemory:true);
+ Assert.IsAssignableFrom(inMemoryConfig.RouteConstraintFactory);
+ }
+
+ [Test]
+ public void Issue191_default_web_config_inits_web_http_constraint_factory()
+ {
+ var inMemoryConfig = new HttpWebConfiguration();
+ Assert.IsAssignableFrom(inMemoryConfig.RouteConstraintFactory);
+ }
+ }
+}
From f43eab03021ffdb490381ab55d8b204b109ffcc1 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sat, 23 Feb 2013 19:04:45 -0700
Subject: [PATCH 64/97] cleaning up
---
.../Framework/AttributeRouteVisitor.cs | 35 +++++++++++------
.../Framework/RouteBuilder.cs | 39 ++++++++++++-------
2 files changed, 48 insertions(+), 26 deletions(-)
diff --git a/src/AttributeRouting/Framework/AttributeRouteVisitor.cs b/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
index 567c02e..739c1aa 100644
--- a/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
+++ b/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
@@ -16,10 +16,14 @@ namespace AttributeRouting.Framework
/// System.Web.Routing (used for MVC controller routes) and
/// System.Web.Http.Routing (used for Web API controller routes).
///
- public class AttributeRouteVisitor
- {
+ public class AttributeRouteVisitor
+ {
+ private static readonly Lazy PathAndQueryRegex =
+ new Lazy(() => new Regex(@"(?[^\?]*)(?\?.*)?"));
+
private readonly IAttributeRoute _route;
- private readonly ConfigurationBase _configuration;
+ private readonly ConfigurationBase _configuration;
+ private string _staticLeftPartOfUrl;
///
/// Creates a new visitor extending implementations of IAttributeRoute with common logic.
@@ -34,6 +38,21 @@ public AttributeRouteVisitor(IAttributeRoute route, ConfigurationBase configurat
_route = route;
_configuration = configuration;
}
+
+ private string StaticLeftPartOfUrl
+ {
+ get
+ {
+ if (_staticLeftPartOfUrl == null)
+ {
+ var routePath = _route.Url;
+ var indexOfFirstParam = routePath.IndexOf("{", StringComparison.OrdinalIgnoreCase);
+ var leftPart = (indexOfFirstParam == -1) ? routePath : routePath.Substring(0, indexOfFirstParam);
+ _staticLeftPartOfUrl = leftPart.TrimEnd('/');
+ }
+ return _staticLeftPartOfUrl;
+ }
+ }
///
/// Performs lowercasing and appends trailing slash if this route is so configured.
@@ -226,15 +245,9 @@ public bool IsCultureNameMatched(string cultureName)
/// Thanks: http://samsaffron.com/archive/2011/10/13/optimising-asp-net-mvc3-routing
public bool IsStaticLeftPartOfUrlMatched(string requestedPath)
{
- // Get the static left part of the route's url.
- var routePath = _route.Url;
- var indexOfFirstParam = routePath.IndexOf("{", StringComparison.OrdinalIgnoreCase);
- var leftPart = indexOfFirstParam == -1 ? routePath : routePath.Substring(0, indexOfFirstParam);
- var staticLeftPartOfUrl = leftPart.TrimEnd('/');
-
// Compare the left part with the requested path
var comparableRequestedPath = requestedPath.TrimEnd('/');
- return comparableRequestedPath.StartsWith(staticLeftPartOfUrl, StringComparison.OrdinalIgnoreCase);
+ return comparableRequestedPath.StartsWith(StaticLeftPartOfUrl, StringComparison.OrdinalIgnoreCase);
}
///
@@ -283,7 +296,7 @@ private static string AppendTrailingSlashToVirtualPath(string virtualPath)
private static void GetPathAndQuery(string virtualPath, out string path, out string query)
{
// NOTE: Do not lowercase the querystring vals
- var match = Regex.Match(virtualPath, @"(?[^\?]*)(?\?.*)?");
+ var match = PathAndQueryRegex.Value.Match(virtualPath);
// Just covering my backside here in case the regex fails for some reason.
if (!match.Success)
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 8170dbc..5b9f212 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -11,8 +11,26 @@ namespace AttributeRouting.Framework
/// Creates objects according to the
/// options set in implementations of .
///
- public class RouteBuilder
- {
+ public class RouteBuilder
+ {
+ private static readonly Lazy ConstraintParamsRegex =
+ new Lazy(() => new Regex(@"^.*\(.*\)$", RegexOptions.Compiled));
+
+ private static readonly Lazy DetokenizeUrlRegex =
+ new Lazy(() =>
+ {
+ var patterns = new List
+ {
+ @"(?<=\{)\?", // leading question mark (used to specify optional param)
+ @"\?(?=\})", // trailing question mark (used to specify optional param)
+ @"\(.*?\)(?=\})", // stuff inside parens (used to specify inline regex route constraint)
+ @"\:(.*?)(\(.*?\))?((?=\})|(?=\?\}))", // new inline constraint syntax
+ @"(?<=\{.*)=.*?(?=\})", // equals and value (used to specify inline parameter default value)
+ };
+ var pattern = String.Join("|", patterns);
+ return new Regex(pattern);
+ });
+
private readonly ConfigurationBase _configuration;
private readonly IParameterFactory _parameterFactory;
private readonly IAttributeRouteFactory _routeFactory;
@@ -167,7 +185,7 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
string constraintName;
object constraint;
- if (Regex.IsMatch(definition, @"^.*\(.*\)$"))
+ if (ConstraintParamsRegex.Value.IsMatch(definition))
{
// Constraint of the form "firstName:string(50)"
var indexOfOpenParen = definition.IndexOf('(');
@@ -458,16 +476,7 @@ private string CreateRouteUrl(string routeUrl, string routePrefix, string areaUr
private static string DetokenizeUrl(string url)
{
- var patterns = new List
- {
- @"(?<=\{)\?", // leading question mark (used to specify optional param)
- @"\?(?=\})", // trailing question mark (used to specify optional param)
- @"\(.*?\)(?=\})", // stuff inside parens (used to specify inline regex route constraint)
- @"\:(.*?)(\(.*?\))?((?=\})|(?=\?\}))", // new inline constraint syntax
- @"(?<=\{.*)=.*?(?=\})", // equals and value (used to specify inline parameter default value)
- };
-
- return Regex.Replace(url, String.Join("|", patterns), "");
+ return DetokenizeUrlRegex.Value.Replace(url, "");
}
private static IEnumerable GetUrlParameterContents(string url)
@@ -507,8 +516,8 @@ private static IEnumerable GetUrlParameterContents(string url)
}
}
- private static string RemoveQueryString(string url)
- {
+ private static string RemoveQueryString(string url)
+ {
// Must honor ? in regex expressions and as used to specify optional params,
// So run through the url chars and fast forward when inside a url param (eg: {...})
for (int i = 0, length = url.Length; i < length; i++)
From 4884f580abdfa0506293a303d90b72d2b438cf81 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Thu, 28 Feb 2013 11:07:26 -0700
Subject: [PATCH 65/97] updated readme and asembly info for 3.5
---
README.textile | 5 +++++
src/SharedAssemblyInfo.cs | 6 +++---
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/README.textile b/README.textile
index 142ff3d..0c0676d 100644
--- a/README.textile
+++ b/README.textile
@@ -4,6 +4,11 @@ h2. Please refer to "attributerouting.net":http://attributerouting.net/ for docu
h3. Changelog
+_3.5_
+
+* #191 - Enabled in-memory hosting of web-hosted web api routes.
+* Various perf enhancements and code reorganization.
+
_3.4.2_
* #192 - BUG FIX: Optional parameters weren't working in asp.net web api action urls.
diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs
index b19e4c9..ae5bfcc 100644
--- a/src/SharedAssemblyInfo.cs
+++ b/src/SharedAssemblyInfo.cs
@@ -7,7 +7,7 @@
[assembly: AssemblyProduct("AttributeRouting")]
[assembly: AssemblyCopyright("Copyright 2010-2013 Tim McCall")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("3.4.2")]
-[assembly: AssemblyFileVersion("3.4.2")]
-[assembly: AssemblyInformationalVersion("3.4.2")]
+[assembly: AssemblyVersion("3.5")]
+[assembly: AssemblyFileVersion("3.5")]
+[assembly: AssemblyInformationalVersion("3.5")]
[assembly: AssemblyConfiguration("Release")]
From 59230ea7ec49bb5d9202e80f31f4085df46dca39 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sat, 2 Mar 2013 11:24:39 -0700
Subject: [PATCH 66/97] #206 - changed the name of the app start files to
prevent ns conflicts.
---
.../AttributeRouting.WebApi.Hosted.nutrans | 4 ++--
.../{AttributeRouting.cs.pp => AttributeRoutingConfig.cs.pp} | 2 +-
.../{AttributeRouting.vb.pp => AttributeRoutingConfig.vb.pp} | 2 +-
nuget/AttributeRouting.WebApi.Hosted/install.ps1 | 4 ++--
nuget/AttributeRouting.WebApi/AttributeRouting.WebApi.nutrans | 4 ++--
...buteRoutingHttp.cs.pp => AttributeRoutingHttpConfig.cs.pp} | 4 ++--
...buteRoutingHttp.vb.pp => AttributeRoutingHttpConfig.vb.pp} | 4 ++--
nuget/AttributeRouting.WebApi/install.ps1 | 4 ++--
nuget/AttributeRouting/AttributeRouting.nutrans | 4 ++--
.../{AttributeRouting.cs.pp => AttributeRoutingConfig.cs.pp} | 4 ++--
.../{AttributeRouting.vb.pp => AttributeRoutingConfig.vb.pp} | 4 ++--
nuget/AttributeRouting/install.ps1 | 4 ++--
12 files changed, 22 insertions(+), 22 deletions(-)
rename nuget/AttributeRouting.WebApi.Hosted/{AttributeRouting.cs.pp => AttributeRoutingConfig.cs.pp} (93%)
rename nuget/AttributeRouting.WebApi.Hosted/{AttributeRouting.vb.pp => AttributeRoutingConfig.vb.pp} (94%)
rename nuget/AttributeRouting.WebApi/{AttributeRoutingHttp.cs.pp => AttributeRoutingHttpConfig.cs.pp} (85%)
rename nuget/AttributeRouting.WebApi/{AttributeRoutingHttp.vb.pp => AttributeRoutingHttpConfig.vb.pp} (87%)
rename nuget/AttributeRouting/{AttributeRouting.cs.pp => AttributeRoutingConfig.cs.pp} (85%)
rename nuget/AttributeRouting/{AttributeRouting.vb.pp => AttributeRoutingConfig.vb.pp} (87%)
diff --git a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.WebApi.Hosted.nutrans b/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.WebApi.Hosted.nutrans
index 36bc6ff..ee78294 100644
--- a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.WebApi.Hosted.nutrans
+++ b/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.WebApi.Hosted.nutrans
@@ -16,8 +16,8 @@
-
-
+
+
\ No newline at end of file
diff --git a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.cs.pp b/nuget/AttributeRouting.WebApi.Hosted/AttributeRoutingConfig.cs.pp
similarity index 93%
rename from nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.cs.pp
rename to nuget/AttributeRouting.WebApi.Hosted/AttributeRoutingConfig.cs.pp
index 5d727f7..6ffbb81 100644
--- a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.cs.pp
+++ b/nuget/AttributeRouting.WebApi.Hosted/AttributeRoutingConfig.cs.pp
@@ -3,7 +3,7 @@
namespace $rootnamespace$
{
- public static class AttributeRouting
+ public static class AttributeRoutingConfig
{
// Call this static method from a start up class in your applicaton (e.g.Program.cs)
// Pass in the configuration you're using for your self-hosted Web API
diff --git a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.vb.pp b/nuget/AttributeRouting.WebApi.Hosted/AttributeRoutingConfig.vb.pp
similarity index 94%
rename from nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.vb.pp
rename to nuget/AttributeRouting.WebApi.Hosted/AttributeRoutingConfig.vb.pp
index 870bf2d..5d1921c 100644
--- a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.vb.pp
+++ b/nuget/AttributeRouting.WebApi.Hosted/AttributeRoutingConfig.vb.pp
@@ -2,7 +2,7 @@
Imports AttributeRouting.Web.Http.SelfHost
Namespace $rootnamespace$
- Public Class AttributeRouting
+ Public Class AttributeRoutingConfig
' Call this static method from a start up class in your applicaton (e.g.Program.cs)
' Pass in the configuration you're using for your self-hosted Web API
diff --git a/nuget/AttributeRouting.WebApi.Hosted/install.ps1 b/nuget/AttributeRouting.WebApi.Hosted/install.ps1
index 0871aa9..7ff35bd 100644
--- a/nuget/AttributeRouting.WebApi.Hosted/install.ps1
+++ b/nuget/AttributeRouting.WebApi.Hosted/install.ps1
@@ -2,7 +2,7 @@
# Remove the App_Start file for other languages
if ($project.Type -eq "C#") {
- $project.ProjectItems.Item("AttributeRouting.vb").Delete();
+ $project.ProjectItems.Item("AttributeRoutingConfig.vb").Delete();
} elseif ($project.Type -eq "VB.NET") {
- $project.ProjectItems.Item("AttributeRouting.cs").Delete();
+ $project.ProjectItems.Item("AttributeRoutingConfig.cs").Delete();
}
\ No newline at end of file
diff --git a/nuget/AttributeRouting.WebApi/AttributeRouting.WebApi.nutrans b/nuget/AttributeRouting.WebApi/AttributeRouting.WebApi.nutrans
index 9669fa3..8abb26b 100644
--- a/nuget/AttributeRouting.WebApi/AttributeRouting.WebApi.nutrans
+++ b/nuget/AttributeRouting.WebApi/AttributeRouting.WebApi.nutrans
@@ -18,8 +18,8 @@
-
-
+
+
diff --git a/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.cs.pp b/nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.cs.pp
similarity index 85%
rename from nuget/AttributeRouting.WebApi/AttributeRoutingHttp.cs.pp
rename to nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.cs.pp
index 30685a9..dee2cb4 100644
--- a/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.cs.pp
+++ b/nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.cs.pp
@@ -1,11 +1,11 @@
using System.Web.Http;
using AttributeRouting.Web.Http.WebHost;
-[assembly: WebActivator.PreApplicationStartMethod(typeof($rootnamespace$.AttributeRoutingHttp), "Start")]
+[assembly: WebActivator.PreApplicationStartMethod(typeof($rootnamespace$.AttributeRoutingHttpConfig), "Start")]
namespace $rootnamespace$
{
- public static class AttributeRoutingHttp
+ public static class AttributeRoutingHttpConfig
{
public static void RegisterRoutes(HttpRouteCollection routes)
{
diff --git a/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.vb.pp b/nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.vb.pp
similarity index 87%
rename from nuget/AttributeRouting.WebApi/AttributeRoutingHttp.vb.pp
rename to nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.vb.pp
index 43c1cbe..d2847bb 100644
--- a/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.vb.pp
+++ b/nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.vb.pp
@@ -1,10 +1,10 @@
Imports System.Web.Http
Imports AttributeRouting.Web.Http.WebHost
-
+
Namespace $rootnamespace$
- Public Class AttributeRoutingHttp
+ Public Class AttributeRoutingHttpConfig
Public Shared Sub RegisterRoutes(routes As HttpRouteCollection)
' See http://github.com/mccalltd/AttributeRouting/wiki for more options.
diff --git a/nuget/AttributeRouting.WebApi/install.ps1 b/nuget/AttributeRouting.WebApi/install.ps1
index 93dbf73..b979922 100644
--- a/nuget/AttributeRouting.WebApi/install.ps1
+++ b/nuget/AttributeRouting.WebApi/install.ps1
@@ -4,7 +4,7 @@ $appStartTemplatesFolder = $project.ProjectItems.Item("App_Start");
# Remove the App_Start file for other languages
if ($project.Type -eq "C#") {
- $appStartTemplatesFolder.ProjectItems.Item("AttributeRoutingHttp.vb").Delete();
+ $appStartTemplatesFolder.ProjectItems.Item("AttributeRoutingHttpConfig.vb").Delete();
} elseif ($project.Type -eq "VB.NET") {
- $appStartTemplatesFolder.ProjectItems.Item("AttributeRoutingHttp.cs").Delete();
+ $appStartTemplatesFolder.ProjectItems.Item("AttributeRoutingHttpConfig.cs").Delete();
}
\ No newline at end of file
diff --git a/nuget/AttributeRouting/AttributeRouting.nutrans b/nuget/AttributeRouting/AttributeRouting.nutrans
index 6adfdb6..7629458 100644
--- a/nuget/AttributeRouting/AttributeRouting.nutrans
+++ b/nuget/AttributeRouting/AttributeRouting.nutrans
@@ -16,8 +16,8 @@
-
-
+
+
diff --git a/nuget/AttributeRouting/AttributeRouting.cs.pp b/nuget/AttributeRouting/AttributeRoutingConfig.cs.pp
similarity index 85%
rename from nuget/AttributeRouting/AttributeRouting.cs.pp
rename to nuget/AttributeRouting/AttributeRoutingConfig.cs.pp
index 56d76cf..8458862 100644
--- a/nuget/AttributeRouting/AttributeRouting.cs.pp
+++ b/nuget/AttributeRouting/AttributeRoutingConfig.cs.pp
@@ -1,11 +1,11 @@
using System.Web.Routing;
using AttributeRouting.Web.Mvc;
-[assembly: WebActivator.PreApplicationStartMethod(typeof($rootnamespace$.AttributeRouting), "Start")]
+[assembly: WebActivator.PreApplicationStartMethod(typeof($rootnamespace$.AttributeRoutingConfig), "Start")]
namespace $rootnamespace$
{
- public static class AttributeRouting
+ public static class AttributeRoutingConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
diff --git a/nuget/AttributeRouting/AttributeRouting.vb.pp b/nuget/AttributeRouting/AttributeRoutingConfig.vb.pp
similarity index 87%
rename from nuget/AttributeRouting/AttributeRouting.vb.pp
rename to nuget/AttributeRouting/AttributeRoutingConfig.vb.pp
index 5c44b81..91ff486 100644
--- a/nuget/AttributeRouting/AttributeRouting.vb.pp
+++ b/nuget/AttributeRouting/AttributeRoutingConfig.vb.pp
@@ -1,10 +1,10 @@
Imports System.Web.Routing
Imports AttributeRouting.Web.Mvc
-
+
Namespace $rootnamespace$
- Public Class AttributeRouting
+ Public Class AttributeRoutingConfig
Public Shared Sub RegisterRoutes(routes As RouteCollection)
' See http://github.com/mccalltd/AttributeRouting/wiki for more options.
diff --git a/nuget/AttributeRouting/install.ps1 b/nuget/AttributeRouting/install.ps1
index b97e57d..d1b6d64 100644
--- a/nuget/AttributeRouting/install.ps1
+++ b/nuget/AttributeRouting/install.ps1
@@ -4,7 +4,7 @@ $appStartTemplatesFolder = $project.ProjectItems.Item("App_Start");
# Remove the App_Start file for other languages
if ($project.Type -eq "C#") {
- $appStartTemplatesFolder.ProjectItems.Item("AttributeRouting.vb").Delete();
+ $appStartTemplatesFolder.ProjectItems.Item("AttributeRoutingConfig.vb").Delete();
} elseif ($project.Type -eq "VB.NET") {
- $appStartTemplatesFolder.ProjectItems.Item("AttributeRouting.cs").Delete();
+ $appStartTemplatesFolder.ProjectItems.Item("AttributeRoutingConfig.cs").Delete();
}
\ No newline at end of file
From 31f894f0c95fd91afbcaeea8416205d355e5ca6b Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sat, 2 Mar 2013 12:29:00 -0700
Subject: [PATCH 67/97] #199 - when applying inbound http method constraint,
now checking the unvalidated form/query collection.
---
.../Controllers/DangerController.cs | 3 +-
.../InboundHttpMethodConstraint.cs | 8 ++
.../AttributeRouting.Web.Mvc.csproj | 25 ++++++
.../InboundHttpMethodConstraint.cs | 54 +++++++++++--
src/AttributeRouting.Web.Mvc/packages.config | 6 ++
.../AttributeRouting.Web.csproj | 1 -
.../Helpers/HttpRequestBaseExtensions.cs | 80 -------------------
7 files changed, 87 insertions(+), 90 deletions(-)
create mode 100644 src/AttributeRouting.Web.Mvc/packages.config
delete mode 100644 src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs
diff --git a/src/AttributeRouting.Tests.Web/Controllers/DangerController.cs b/src/AttributeRouting.Tests.Web/Controllers/DangerController.cs
index 0906b04..9d4b0b7 100644
--- a/src/AttributeRouting.Tests.Web/Controllers/DangerController.cs
+++ b/src/AttributeRouting.Tests.Web/Controllers/DangerController.cs
@@ -13,7 +13,8 @@ public ActionResult Index()
}
[POST("")]
- public ActionResult Index_Post()
+ [ValidateInput(false)]
+ public ActionResult Index_Post(string badstuff)
{
return Content("You survived the danger!");
}
diff --git a/src/AttributeRouting.Web.Http/Constraints/InboundHttpMethodConstraint.cs b/src/AttributeRouting.Web.Http/Constraints/InboundHttpMethodConstraint.cs
index dd88006..f24b482 100644
--- a/src/AttributeRouting.Web.Http/Constraints/InboundHttpMethodConstraint.cs
+++ b/src/AttributeRouting.Web.Http/Constraints/InboundHttpMethodConstraint.cs
@@ -20,6 +20,14 @@ public InboundHttpMethodConstraint(params HttpMethod[] allowedMethods)
ICollection IInboundHttpMethodConstraint.AllowedMethods
{
get { return new ReadOnlyCollection(AllowedMethods.Select(method => method.Method).ToList()); }
+ }
+
+ protected override bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection)
+ {
+ if (routeDirection == HttpRouteDirection.UriGeneration)
+ return true;
+
+ return base.Match(request, route, parameterName, values, routeDirection);
}
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj b/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
index cd3d920..3445cce 100644
--- a/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
+++ b/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj
@@ -43,12 +43,36 @@
AttributeRouting.snk
+
+ True
+ ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll
+
+
+ True
+ ..\packages\Microsoft.AspNet.WebPages.1.0.20105.408\lib\net40\System.Web.Helpers.dll
+
+
+ True
+ ..\packages\Microsoft.AspNet.Razor.1.0.20105.408\lib\net40\System.Web.Razor.dll
+
+
+ True
+ ..\packages\Microsoft.AspNet.WebPages.1.0.20105.408\lib\net40\System.Web.WebPages.dll
+
+
+ True
+ ..\packages\Microsoft.AspNet.WebPages.1.0.20105.408\lib\net40\System.Web.WebPages.Deployment.dll
+
+
+ True
+ ..\packages\Microsoft.AspNet.WebPages.1.0.20105.408\lib\net40\System.Web.WebPages.Razor.dll
+
@@ -78,6 +102,7 @@
+
diff --git a/src/AttributeRouting.Web.Mvc/Constraints/InboundHttpMethodConstraint.cs b/src/AttributeRouting.Web.Mvc/Constraints/InboundHttpMethodConstraint.cs
index 0b48fd3..610347c 100644
--- a/src/AttributeRouting.Web.Mvc/Constraints/InboundHttpMethodConstraint.cs
+++ b/src/AttributeRouting.Web.Mvc/Constraints/InboundHttpMethodConstraint.cs
@@ -1,10 +1,10 @@
using System.Linq;
-using System.Web;
-using System.Web.Mvc;
+using System.Web;
+using System.Web.Helpers;
using System.Web.Routing;
using AttributeRouting.Constraints;
-using AttributeRouting.Helpers;
-
+using AttributeRouting.Helpers;
+
namespace AttributeRouting.Web.Mvc.Constraints
{
public class InboundHttpMethodConstraint : HttpMethodConstraint, IInboundHttpMethodConstraint
@@ -17,16 +17,54 @@ public InboundHttpMethodConstraint(params string[] allowedMethods)
{
}
- protected override bool Match(HttpContextBase httpContext, Route route, string parameterName,
- RouteValueDictionary values, RouteDirection routeDirection)
+ protected override bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (routeDirection == RouteDirection.UrlGeneration)
return true;
- // Make sure to check for HTTP method overrides!
- var httpMethod = httpContext.Request.GetHttpMethodOverride();
+ // Make sure to check for HTTP method overrides!
+ var httpMethod = GetUnvalidatedHttpMethodOverride(httpContext.Request);
return AllowedMethods.Any(m => m.ValueEquals(httpMethod));
+ }
+
+ ///
+ /// The reason we have our own is to provide support for System.Web.Helpers.Validation.Unvalidated calls.
+ /// NOTE: This won't be needed once AR targets .NET 4.5.
+ ///
+ private static string GetUnvalidatedHttpMethodOverride(HttpRequestBase request)
+ {
+ var httpMethod = request.HttpMethod;
+
+ // If not a post, method overrides don't apply.
+ if (!httpMethod.ValueEquals("POST"))
+ {
+ return httpMethod;
+ }
+
+ // Get the method override and if it's not for a GET or POST, then apply it.
+ var methodOverride = request.SafeGet(r => r.Headers["X-HTTP-Method-Override"]) ??
+ request.SafeGet(r => GetFormValue(r, "X-HTTP-Method-Override")) ??
+ request.SafeGet(r => GetQueryStringValue(r, "X-HTTP-Method-Override"));
+
+ if (methodOverride != null &&
+ (!methodOverride.ValueEquals("GET") && !methodOverride.ValueEquals("POST")))
+ {
+ return methodOverride;
+ }
+
+ // Otherwise, just return the http method.
+ return httpMethod;
+ }
+
+ private static string GetFormValue(HttpRequestBase request, string key)
+ {
+ return request.Unvalidated().QueryString[key] ?? request.Form[key];
+ }
+
+ private static string GetQueryStringValue(HttpRequestBase request, string key)
+ {
+ return request.Unvalidated().Form[key] ?? request.QueryString[key];
}
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Mvc/packages.config b/src/AttributeRouting.Web.Mvc/packages.config
new file mode 100644
index 0000000..77411dd
--- /dev/null
+++ b/src/AttributeRouting.Web.Mvc/packages.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AttributeRouting.Web/AttributeRouting.Web.csproj b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
index 7d13799..9bb3a21 100644
--- a/src/AttributeRouting.Web/AttributeRouting.Web.csproj
+++ b/src/AttributeRouting.Web/AttributeRouting.Web.csproj
@@ -74,7 +74,6 @@
-
diff --git a/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs b/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs
deleted file mode 100644
index 6f48dbe..0000000
--- a/src/AttributeRouting.Web/Helpers/HttpRequestBaseExtensions.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using System.Collections.Specialized;
-using System.Reflection;
-using System.Web;
-using AttributeRouting.Helpers;
-
-namespace AttributeRouting.Web.Helpers
-{
- public static class HttpRequestBaseExtensions
- {
- private static bool _isSystemWebWebPagesUnavailable;
-
- public static string GetFormValue(this HttpRequestBase request, string key)
- {
- return request.GetUnvalidatedCollectionValue("Form", key) ?? request.Form[key];
- }
-
- public static string GetQueryStringValue(this HttpRequestBase request, string key)
- {
- return request.GetUnvalidatedCollectionValue("QueryString", key) ?? request.QueryString[key];
- }
-
- ///
- /// Loads the Form or QueryString collection value from the unvalidated object in System.Web.Webpages,
- /// if that assembly is available.
- ///
- private static string GetUnvalidatedCollectionValue(this HttpRequestBase request, string unvalidatedObjectPropertyName, string key)
- {
- if (_isSystemWebWebPagesUnavailable)
- {
- return null;
- }
-
- try
- {
- var webPagesAssembly = Assembly.Load("System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
- var validationType = webPagesAssembly.GetType("System.Web.Helpers.Validation");
- var unvalidatedMethod = validationType.GetMethod("Unvalidated", new[] { request.GetType() });
- var unvalidatedObject = unvalidatedMethod.Invoke(null, new[] { request });
- var collectionProperty = unvalidatedObject.GetType().GetProperty(unvalidatedObjectPropertyName);
- var collection = (NameValueCollection)collectionProperty.GetValue(unvalidatedObject, null);
-
- return collection[key];
- }
- catch
- {
- _isSystemWebWebPagesUnavailable = true;
-
- return null;
- }
- }
-
- ///
- /// The reason we have our own is to provide support for System.Web.Helpers.Validation.Unvalidated calls.
- ///
- public static string GetHttpMethod(this HttpRequestBase request)
- {
- var httpMethod = request.HttpMethod;
-
- // If not a post, method overrides don't apply.
- if (!httpMethod.ValueEquals("POST"))
- {
- return httpMethod;
- }
-
- // Get the method override and if it's not for a GET or POST, then apply it.
- var methodOverride = request.SafeGet(r => r.Headers["X-HTTP-Method-Override"]) ??
- request.SafeGet(r => GetFormValue(r, "X-HTTP-Method-Override")) ??
- request.SafeGet(r => GetQueryStringValue(r, "X-HTTP-Method-Override"));
-
- if (methodOverride != null &&
- (!methodOverride.ValueEquals("GET") && !methodOverride.ValueEquals("POST")))
- {
- return methodOverride;
- }
-
- // Otherwise, just return the http method.
- return httpMethod;
- }
- }
-}
From 7d77f7b6cbbe35252773b2665111facea64c38cd Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sat, 2 Mar 2013 13:31:25 -0700
Subject: [PATCH 68/97] #199 - added nuget dependency on webpages for mvc
package.
---
nuget/AttributeRouting/AttributeRouting.nutrans | 1 +
1 file changed, 1 insertion(+)
diff --git a/nuget/AttributeRouting/AttributeRouting.nutrans b/nuget/AttributeRouting/AttributeRouting.nutrans
index 7629458..1fe56e8 100644
--- a/nuget/AttributeRouting/AttributeRouting.nutrans
+++ b/nuget/AttributeRouting/AttributeRouting.nutrans
@@ -8,6 +8,7 @@
+
From ad153213504b97b67e9d136440af31f947f49e7b Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sat, 2 Mar 2013 14:04:57 -0700
Subject: [PATCH 69/97] test controllers for web api action inheritence.
---
.../Controllers/InheritActionsControllers.cs | 17 +++++++++++++++++
.../AttributeRouting.Tests.Web.csproj | 1 +
2 files changed, 18 insertions(+)
create mode 100644 src/AttributeRouting.Tests.Web/Areas/Api/Controllers/InheritActionsControllers.cs
diff --git a/src/AttributeRouting.Tests.Web/Areas/Api/Controllers/InheritActionsControllers.cs b/src/AttributeRouting.Tests.Web/Areas/Api/Controllers/InheritActionsControllers.cs
new file mode 100644
index 0000000..550f022
--- /dev/null
+++ b/src/AttributeRouting.Tests.Web/Areas/Api/Controllers/InheritActionsControllers.cs
@@ -0,0 +1,17 @@
+using System.Web.Http;
+using AttributeRouting.Web.Http;
+
+namespace AttributeRouting.Tests.Web.Areas.Api.Controllers
+{
+ public abstract class InheritActionsApiControllerBase : BaseApiController
+ {
+ [GET("Base-Method"), HttpGet]
+ public string Index()
+ {
+ return "Base Index";
+ }
+ }
+
+ [RoutePrefix("Inherit/Derived")]
+ public class InheritActionsDerivedApiController : InheritActionsApiControllerBase {}
+}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj b/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj
index 2e8a215..145b078 100644
--- a/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj
+++ b/src/AttributeRouting.Tests.Web/AttributeRouting.Tests.Web.csproj
@@ -119,6 +119,7 @@
+
From b9e35f580b87f5ea1bad3c04e5e06d22b5c4acd1 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sat, 2 Mar 2013 14:09:38 -0700
Subject: [PATCH 70/97] updated readme/assemblyinfo for 3.5.1
---
README.textile | 5 +++++
src/SharedAssemblyInfo.cs | 6 +++---
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/README.textile b/README.textile
index 0c0676d..bf02d57 100644
--- a/README.textile
+++ b/README.textile
@@ -4,6 +4,11 @@ h2. Please refer to "attributerouting.net":http://attributerouting.net/ for docu
h3. Changelog
+_3.5.1_
+
+* #199 - when applying inbound http method constraint, now checking the unvalidated form/query collection.
+* #206 - changed the name of the app start files to prevent ns conflicts.
+
_3.5_
* #191 - Enabled in-memory hosting of web-hosted web api routes.
diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs
index ae5bfcc..494d17d 100644
--- a/src/SharedAssemblyInfo.cs
+++ b/src/SharedAssemblyInfo.cs
@@ -7,7 +7,7 @@
[assembly: AssemblyProduct("AttributeRouting")]
[assembly: AssemblyCopyright("Copyright 2010-2013 Tim McCall")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("3.5")]
-[assembly: AssemblyFileVersion("3.5")]
-[assembly: AssemblyInformationalVersion("3.5")]
+[assembly: AssemblyVersion("3.5.1")]
+[assembly: AssemblyFileVersion("3.5.1")]
+[assembly: AssemblyInformationalVersion("3.5.1")]
[assembly: AssemblyConfiguration("Release")]
From 01574c67e3be637383f84cfbdc1d5d287d535ece Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Fri, 8 Mar 2013 12:12:11 -0700
Subject: [PATCH 71/97] fixed logic for creating and testing query string
constraints so thread safety is not violated.
---
.../Features/RouteConstraints.feature | 24 +++--
.../Features/RouteConstraints.feature.cs | 40 +++++----
.../Steps/RouteConstraintSteps.cs | 5 +-
.../Framework/HttpAttributeRoute.cs | 16 +++-
.../Framework/AttributeRoute.cs | 18 ++--
.../Constraints/RegexRouteConstraintBase.cs | 4 +-
.../Framework/AttributeRouteVisitor.cs | 90 ++++++++-----------
.../Framework/IAttributeRoute.cs | 5 ++
.../Framework/RouteBuilder.cs | 76 +++++++++-------
9 files changed, 156 insertions(+), 122 deletions(-)
diff --git a/src/AttributeRouting.Specs/Features/RouteConstraints.feature b/src/AttributeRouting.Specs/Features/RouteConstraints.feature
index 9300fce..f4086f5 100644
--- a/src/AttributeRouting.Specs/Features/RouteConstraints.feature
+++ b/src/AttributeRouting.Specs/Features/RouteConstraints.feature
@@ -41,12 +41,12 @@ Scenario: Inline constraints in the querystring
Then the route url is "Inline-Constraints/Querystring"
And the parameter "x" is constrained by an inline AttributeRouting.Web.Constraints.IntRouteConstraint
And the parameter "y" is constrained by an inline AttributeRouting.Web.Constraints.QueryStringRouteConstraint
- # Web API
- Given I have registered the routes for the HttpInlineRouteConstraintsController
- When I fetch the routes for the HttpInlineRouteConstraints controller's Querystring action
- Then the route url is "Http-Inline-Constraints/Querystring"
- And the parameter "x" is constrained by an inline AttributeRouting.Web.Constraints.IntRouteConstraint
- And the parameter "y" is constrained by an inline AttributeRouting.Web.Constraints.QueryStringRouteConstraint
+ # Web API - NOTE: this won't work until web api vNext
+ #Given I have registered the routes for the HttpInlineRouteConstraintsController
+ #When I fetch the routes for the HttpInlineRouteConstraints controller's Querystring action
+ #Then the route url is "Http-Inline-Constraints/Querystring"
+ #And the parameter "x" is constrained by an inline AttributeRouting.Web.Constraints.IntRouteConstraint
+ #And the parameter "y" is constrained by an inline AttributeRouting.Web.Constraints.QueryStringRouteConstraint
Scenario: Multiple inline constraints per url segment
# MVC
@@ -147,6 +147,18 @@ Scenario Outline: Matching inline route constraints
| EnumValue/10 | EnumValue | is not |
| WithOptional | WithOptional | is |
| WithDefault | WithDefault | is |
+
+Scenario Outline: Matching inline route constraints in the querystring
+ # MVC
+ Given I have registered the routes for the InlineRouteConstraintsController
+ When a request for "Inline-Constraints/" is made
+ Then the action matched
+ # Web API - NOTE: these won't work until web api vNext.
+ #Given I have registered the routes for the HttpInlineRouteConstraintsController
+ #When a request for "Http-Inline-Constraints/" is made
+ #Then the action matched
+ Examples:
+ | url | action | condition |
| Querystring?x=123&y=hello | Querystring | is |
| Querystring?x=abc&y=hello | Querystring | is not |
| Querystring?x=abc | Querystring | is not |
diff --git a/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs b/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
index 6af704a..6585750 100644
--- a/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
@@ -3,7 +3,7 @@
// This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:1.9.0.77
// SpecFlow Generator Version:1.9.0.0
-// Runtime Version:4.0.30319.18010
+// Runtime Version:4.0.30319.18034
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -133,19 +133,6 @@ public virtual void InlineConstraintsInTheQuerystring()
#line 43
testRunner.And("the parameter \"y\" is constrained by an inline AttributeRouting.Web.Constraints.Qu" +
"eryStringRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 45
- testRunner.Given("I have registered the routes for the HttpInlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
-#line 46
- testRunner.When("I fetch the routes for the HttpInlineRouteConstraints controller\'s Querystring ac" +
- "tion", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
-#line 47
- testRunner.Then("the route url is \"Http-Inline-Constraints/Querystring\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
-#line 48
- testRunner.And("the parameter \"x\" is constrained by an inline AttributeRouting.Web.Constraints.In" +
- "tRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
-#line 49
- testRunner.And("the parameter \"y\" is constrained by an inline AttributeRouting.Web.Constraints.Qu" +
- "eryStringRouteConstraint", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@@ -301,10 +288,6 @@ public virtual void InlineConstraintsSpecifiedInTheRouteAreaAttribute()
[NUnit.Framework.TestCaseAttribute("EnumValue/10", "EnumValue", "is not", null)]
[NUnit.Framework.TestCaseAttribute("WithOptional", "WithOptional", "is", null)]
[NUnit.Framework.TestCaseAttribute("WithDefault", "WithDefault", "is", null)]
- [NUnit.Framework.TestCaseAttribute("Querystring?x=123&y=hello", "Querystring", "is", null)]
- [NUnit.Framework.TestCaseAttribute("Querystring?x=abc&y=hello", "Querystring", "is not", null)]
- [NUnit.Framework.TestCaseAttribute("Querystring?x=abc", "Querystring", "is not", null)]
- [NUnit.Framework.TestCaseAttribute("Querystring?y=hello", "Querystring", "is not", null)]
public virtual void MatchingInlineRouteConstraints(string url, string action, string condition, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Matching inline route constraints", exampleTags);
@@ -322,6 +305,27 @@ public virtual void MatchingInlineRouteConstraints(string url, string action, st
testRunner.When(string.Format("a request for \"Http-Inline-Constraints/{0}\" is made", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 97
testRunner.Then(string.Format("the {0} action {1} matched", action, condition), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Matching inline route constraints in the querystring")]
+ [NUnit.Framework.TestCaseAttribute("Querystring?x=123&y=hello", "Querystring", "is", null)]
+ [NUnit.Framework.TestCaseAttribute("Querystring?x=abc&y=hello", "Querystring", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Querystring?x=abc", "Querystring", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("Querystring?y=hello", "Querystring", "is not", null)]
+ public virtual void MatchingInlineRouteConstraintsInTheQuerystring(string url, string action, string condition, string[] exampleTags)
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Matching inline route constraints in the querystring", exampleTags);
+#line 151
+this.ScenarioSetup(scenarioInfo);
+#line 153
+ testRunner.Given("I have registered the routes for the InlineRouteConstraintsController", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 154
+ testRunner.When(string.Format("a request for \"Inline-Constraints/{0}\" is made", url), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 155
+ testRunner.Then(string.Format("the {0} action {1} matched", action, condition), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
diff --git a/src/AttributeRouting.Specs/Steps/RouteConstraintSteps.cs b/src/AttributeRouting.Specs/Steps/RouteConstraintSteps.cs
index 943df39..f48541a 100644
--- a/src/AttributeRouting.Specs/Steps/RouteConstraintSteps.cs
+++ b/src/AttributeRouting.Specs/Steps/RouteConstraintSteps.cs
@@ -34,7 +34,10 @@ public void ThenTheParameterIsConstrainedByAnInline(string key, string type)
Assert.That(route, Is.Not.Null);
var constraint = route.Constraints[key];
-
+ if (constraint == null && route is IAttributeRoute)
+ {
+ constraint = ((IAttributeRoute)route).QueryStringConstraints[key];
+ }
Assert.That(constraint, Is.Not.Null);
// If this is a querystring route constraint wrapper, then unwrap it.
diff --git a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
index 602653b..b56ec0a 100644
--- a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
+++ b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Net.Http;
using System.Web.Http.Routing;
+using System.Web.Routing;
using AttributeRouting.Framework;
using AttributeRouting.Helpers;
@@ -32,6 +32,7 @@ public HttpAttributeRoute(string url,
{
_configuration = configuration;
_visitor = new AttributeRouteVisitor(this, configuration);
+ QueryStringConstraints = new RouteValueDictionary();
}
public bool? AppendTrailingSlash { get; set; }
@@ -59,6 +60,8 @@ IDictionary IAttributeRoute.Defaults
public List MappedSubdomains { get; set; }
public bool? PreserveCaseForUrlParameters { get; set; }
+
+ public IDictionary QueryStringConstraints { get; set; }
public string RouteName { get; set; }
@@ -92,6 +95,13 @@ public override IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestM
return null;
}
+ // Constrain by querystring param if there are any.
+ var routeValues = new HttpRouteValueDictionary(routeData.Values);
+ if (!_visitor.ProcessQueryStringConstraints((constraint, parameterName) => ProcessConstraint(request, constraint, parameterName, routeValues, HttpRouteDirection.UriResolution)))
+ {
+ return null;
+ }
+
// Constrain by subdomain if configured
var requestedSubdomain = GetCachedValue(request, RequestedSubdomainKey, () => _configuration.SubdomainParser(request.SafeGet(r => r.Headers.Host)));
if (!_visitor.IsSubdomainMatched(requestedSubdomain))
@@ -111,8 +121,8 @@ public override IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestM
public override IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary values)
{
- // Let the underlying route do its thing, and if it does, then add some functionality on top.
- var virtualPathData = _visitor.GetVirtualPath(() => base.GetVirtualPath(request, values));
+ // Let the underlying route do its thing.
+ var virtualPathData = base.GetVirtualPath(request, values);
if (virtualPathData == null)
{
return null;
diff --git a/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs b/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
index 8d387dc..a0e0406 100644
--- a/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
+++ b/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
@@ -31,6 +31,7 @@ public AttributeRoute(string url,
{
_configuration = configuration;
_visitor = new AttributeRouteVisitor(this, configuration);
+ QueryStringConstraints = new RouteValueDictionary();
}
public bool? AppendTrailingSlash { get; set; }
@@ -58,6 +59,8 @@ IDictionary IAttributeRoute.Defaults
public List MappedSubdomains { get; set; }
public bool? PreserveCaseForUrlParameters { get; set; }
+
+ public IDictionary QueryStringConstraints { get; set; }
public string RouteName { get; set; }
@@ -85,15 +88,20 @@ public override RouteData GetRouteData(HttpContextBase httpContext)
return null;
}
- // Constrain by subdomain if configured
- // Get the subdomain from the requested hostname.
+ // Constrain by querystring param if there are any.
+ if (!_visitor.ProcessQueryStringConstraints((constraint, parameterName) => ProcessConstraint(httpContext, constraint, parameterName, routeData.Values, RouteDirection.IncomingRequest)))
+ {
+ return null;
+ }
+
+ // Constrain by subdomain if configured.
var requestedSubdomain = GetCachedValue(httpContext, RequestedSubdomainKey, () => _configuration.SubdomainParser(httpContext.SafeGet(c => c.Request.Headers["host"])));
if (!_visitor.IsSubdomainMatched(requestedSubdomain))
{
return null;
}
- // Constrain by culture name if configured
+ // Constrain by culture name if configured.
var currentUICultureName = GetCachedValue(httpContext, CurrentUICultureNameKey, () => _configuration.CurrentUICultureResolver(httpContext, routeData));
if (!_visitor.IsCultureNameMatched(currentUICultureName))
{
@@ -105,8 +113,8 @@ public override RouteData GetRouteData(HttpContextBase httpContext)
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
- // Let the underlying route do its thing, and if it does, then add some functionality on top.
- var virtualPathData = _visitor.GetVirtualPath(() => base.GetVirtualPath(requestContext, values));
+ // Let the underlying route do its thing.
+ var virtualPathData = base.GetVirtualPath(requestContext, values);
if (virtualPathData == null)
{
return null;
diff --git a/src/AttributeRouting/Constraints/RegexRouteConstraintBase.cs b/src/AttributeRouting/Constraints/RegexRouteConstraintBase.cs
index 2b4334e..4eb6fa8 100644
--- a/src/AttributeRouting/Constraints/RegexRouteConstraintBase.cs
+++ b/src/AttributeRouting/Constraints/RegexRouteConstraintBase.cs
@@ -16,8 +16,8 @@ protected RegexRouteConstraintBase(string pattern)
protected RegexRouteConstraintBase(string pattern, RegexOptions options)
{
Pattern = pattern;
- // shouldn't these be included in the derrived classes by default: RegexOptions.CultureInvariant | RegexOptions.IgnoreCase?
- Options = options; //no need to tell user that it is 'compiled' option...so do not include in public options
+ // REVIEW: Shouldn't these be included in the derived classes by default: RegexOptions.CultureInvariant | RegexOptions.IgnoreCase?
+ Options = options; // No need to tell user that it is 'compiled' option...so do not include in public options
CompiledExpression = new Regex(pattern, options | RegexOptions.Compiled);
}
diff --git a/src/AttributeRouting/Framework/AttributeRouteVisitor.cs b/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
index 739c1aa..cfb4f36 100644
--- a/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
+++ b/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
@@ -1,9 +1,7 @@
-using System;
-using System.Collections.Generic;
+using System;
using System.Linq;
using System.Text.RegularExpressions;
-using System.Threading;
-using AttributeRouting.Constraints;
+using System.Threading;
using AttributeRouting.Helpers;
namespace AttributeRouting.Framework
@@ -18,8 +16,7 @@ namespace AttributeRouting.Framework
///
public class AttributeRouteVisitor
{
- private static readonly Lazy PathAndQueryRegex =
- new Lazy(() => new Regex(@"(?[^\?]*)(?\?.*)?"));
+ private static readonly Regex PathAndQueryRegex = new Regex(@"(?[^\?]*)(?\?.*)?");
private readonly IAttributeRoute _route;
private readonly ConfigurationBase _configuration;
@@ -124,52 +121,8 @@ public TVirtualPathData GetTranslatedVirtualPath(Func
- /// Wraps calls to the GetVirtualPath method of routes, removing querystring route constraints
- /// that would otherwise lead to generated routes with invalid urls.
- ///
- /// The type of virtual path data to be returned.
- /// This varies based on whether the route is a
- /// System.Web.Routing.Route or System.Web.Http.Routing.HttpRoute.
- /// The base GetVirtualPath method call
- /// The result from the base call
- public TVirtualPathData GetVirtualPath(Func fromBaseMethod)
- where TVirtualPathData : class
- {
- // Remove querystring route constraints:
- // the base GetVirtualPath will not inject route params that have constraints into the querystring.
- var queryStringConstraints = new Dictionary();
- var constraintKeys = _route.Constraints.Keys.Select(k => k).ToList();
- foreach (var constraintKey in constraintKeys)
- {
- var constraint = _route.Constraints[constraintKey];
- var constraintToTest = constraint is IOptionalRouteConstraint
- ? ((IOptionalRouteConstraint)constraint).Constraint
- : constraint;
-
- if (!(constraintToTest is IQueryStringRouteConstraint))
- {
- continue;
- }
-
- queryStringConstraints.Add(constraintKey, constraint);
- _route.Constraints.Remove(constraintKey);
- }
-
- // Let the underlying route do its thing.
- var virtualPathData = fromBaseMethod();
-
- // Add the querystring constraints back in.
- foreach (var queryStringConstraint in queryStringConstraints)
- {
- _route.Constraints.Add(queryStringConstraint.Key, queryStringConstraint.Value);
- }
-
- return virtualPathData;
- }
-
+ }
+
///
/// Tests whether the route matches the current UI culture.
///
@@ -280,6 +233,37 @@ public bool IsSubdomainMatched(string requestedSubdomain)
return false;
}
+ ///
+ /// Processes query constraints separately from route constraints.
+ ///
+ ///
+ /// Delegate used to process the query constraints according to the underlying route framework.
+ /// Accepts a constraint and parameter name and returns tru if the constraint passes.
+ ///
+ /// True if all query string constraints pass or if there are none to test.
+ ///
+ /// Need to separate path and query constraints because methods in the web stack
+ /// will not add query params to generated urls if there is a constraint for the param name
+ /// that is not present in the url template. See logic in:
+ /// - System.Web.Http.Routing.HttpParsedRoute.Bind(...)
+ /// - System.Web.Routing.ParsedRoute.Bind(...)
+ ///
+ public bool ProcessQueryStringConstraints(Func processConstraint)
+ {
+ foreach (var queryStringConstraint in _route.QueryStringConstraints)
+ {
+ var parameterName = queryStringConstraint.Key;
+ var constraint = queryStringConstraint.Value;
+
+ if (!processConstraint(constraint, parameterName))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
private static string AppendTrailingSlashToVirtualPath(string virtualPath)
{
string path, query;
@@ -296,7 +280,7 @@ private static string AppendTrailingSlashToVirtualPath(string virtualPath)
private static void GetPathAndQuery(string virtualPath, out string path, out string query)
{
// NOTE: Do not lowercase the querystring vals
- var match = PathAndQueryRegex.Value.Match(virtualPath);
+ var match = PathAndQueryRegex.Match(virtualPath);
// Just covering my backside here in case the regex fails for some reason.
if (!match.Success)
diff --git a/src/AttributeRouting/Framework/IAttributeRoute.cs b/src/AttributeRouting/Framework/IAttributeRoute.cs
index 4474fd9..eac3f4f 100644
--- a/src/AttributeRouting/Framework/IAttributeRoute.cs
+++ b/src/AttributeRouting/Framework/IAttributeRoute.cs
@@ -45,6 +45,11 @@ public interface IAttributeRoute
///
bool? PreserveCaseForUrlParameters { get; set; }
+ ///
+ /// Constraints dictionary for querystring constraints.
+ ///
+ IDictionary QueryStringConstraints { get; set; }
+
///
/// The name of this route, for supporting named routes.
///
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 5b9f212..ca8371b 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -13,23 +13,15 @@ namespace AttributeRouting.Framework
///
public class RouteBuilder
{
- private static readonly Lazy ConstraintParamsRegex =
- new Lazy(() => new Regex(@"^.*\(.*\)$", RegexOptions.Compiled));
+ private static readonly Regex ConstraintParamsRegex = new Regex(@"^.*\(.*\)$");
- private static readonly Lazy DetokenizeUrlRegex =
- new Lazy(() =>
- {
- var patterns = new List
- {
- @"(?<=\{)\?", // leading question mark (used to specify optional param)
- @"\?(?=\})", // trailing question mark (used to specify optional param)
- @"\(.*?\)(?=\})", // stuff inside parens (used to specify inline regex route constraint)
- @"\:(.*?)(\(.*?\))?((?=\})|(?=\?\}))", // new inline constraint syntax
- @"(?<=\{.*)=.*?(?=\})", // equals and value (used to specify inline parameter default value)
- };
- var pattern = String.Join("|", patterns);
- return new Regex(pattern);
- });
+ private static readonly Regex DetokenizeUrlRegex =
+ new Regex(@"(?<=\{)\?" + // leading question mark (used to specify optional param)
+ @"|\?(?=\})" + // trailing question mark (used to specify optional param)
+ @"|\(.*?\)(?=\})" + // stuff inside parens (used to specify inline regex route constraint)
+ @"|\:(.*?)(\(.*?\))?((?=\})|(?=\?\}))" + // new inline constraint syntax
+ @"|(?<=\{.*)=.*?(?=\})" // equals and value (used to specify inline parameter default value)
+ );
private readonly ConfigurationBase _configuration;
private readonly IParameterFactory _parameterFactory;
@@ -92,8 +84,10 @@ private string BuildTokenizedUrl(string routeUrl, string routePrefixUrl, string
private IEnumerable BuildRoutes(RouteSpecification routeSpec)
{
- var defaults = CreateRouteDefaults(routeSpec);
- var constraints = CreateRouteConstraints(routeSpec);
+ var defaults = CreateRouteDefaults(routeSpec);
+ IDictionary constraints;
+ IDictionary queryStringConstraints;
+ CreateRouteConstraints(routeSpec, out constraints, out queryStringConstraints);
var dataTokens = CreateRouteDataTokens(routeSpec);
var url = CreateRouteUrl(defaults, routeSpec);
@@ -106,8 +100,9 @@ private IEnumerable BuildRoutes(RouteSpecification routeSpec)
{
route.RouteName = routeName;
route.DataTokens.Add("routeName", routeName);
- }
-
+ }
+
+ route.QueryStringConstraints = queryStringConstraints;
route.Translations = CreateRouteTranslations(routeSpec);
route.Subdomain = routeSpec.Subdomain;
route.UseLowercaseRoute = routeSpec.UseLowercaseRoute;
@@ -131,9 +126,13 @@ private IEnumerable BuildRoutes(RouteSpecification routeSpec)
}
}
- private IDictionary CreateRouteConstraints(RouteSpecification routeSpec)
+ private void CreateRouteConstraints(RouteSpecification routeSpec, out IDictionary constraints, out IDictionary queryStringConstraints)
{
- var constraints = new Dictionary();
+ // Going to return individual collections for:
+ // - path routes constraints (which will go into the generated route's Constraints prop),
+ // - and query string route constraints (which will not work perfectly with the MS bits, and need special treatment by IAttributeRoute impls).
+ constraints = new Dictionary();
+ queryStringConstraints = new Dictionary();
// Default constraints
if (routeSpec.HttpMethods.Any())
@@ -170,7 +169,7 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
var cleanParameter = parameter.TrimEnd('?').Split('=').FirstOrDefault();
var sections = cleanParameter.SplitAndTrim(":");
- // Do not override default or legacy inline constraints
+ // Do not override default constraints
var parameterName = sections.First();
if (constraints.ContainsKey(parameterName))
{
@@ -185,7 +184,7 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
string constraintName;
object constraint;
- if (ConstraintParamsRegex.Value.IsMatch(definition))
+ if (ConstraintParamsRegex.IsMatch(definition))
{
// Constraint of the form "firstName:string(50)"
var indexOfOpenParen = definition.IndexOf('(');
@@ -241,9 +240,15 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
finalConstraint = _routeConstraintFactory.CreateOptionalRouteConstraint(finalConstraint);
}
- // Apply the constraint to the parameter.
- constraints.Add(parameterName, finalConstraint);
-
+ // Add the constraints to the appropriate collection.
+ if (parameterIsInQueryString)
+ {
+ queryStringConstraints.Add(parameterName, finalConstraint);
+ }
+ else
+ {
+ constraints.Add(parameterName, finalConstraint);
+ }
} // ... go to next parameter
// Globally configured constraints
@@ -263,8 +268,6 @@ private IDictionary CreateRouteConstraints(RouteSpecification ro
constraints.Add(urlParameterName, defaultConstraint.Value);
}
}
-
- return constraints;
}
private IDictionary CreateRouteDataTokens(RouteSpecification routeSpec)
@@ -384,8 +387,12 @@ private IEnumerable CreateRouteTranslations(RouteSpecification
//*********************************************
// Otherwise, build a translated route
- var defaults = CreateRouteDefaults(routeSpec);
- var constraints = CreateRouteConstraints(routeSpec);
+ // REVIEW: Could probably forgo processing defaults, constraints, and data tokens for translated routes.
+
+ var defaults = CreateRouteDefaults(routeSpec);
+ IDictionary constraints;
+ IDictionary queryStringConstraints;
+ CreateRouteConstraints(routeSpec, out constraints, out queryStringConstraints);
var dataTokens = CreateRouteDataTokens(routeSpec);
var routeUrl = CreateRouteUrl(translatedRouteUrl ?? routeSpec.RouteUrl,
translatedRoutePrefix ?? routeSpec.RoutePrefixUrl,
@@ -402,8 +409,9 @@ private IEnumerable CreateRouteTranslations(RouteSpecification
{
translatedRoute.RouteName = routeName;
translatedRoute.DataTokens.Add("routeName", routeName);
- }
-
+ }
+
+ translatedRoute.QueryStringConstraints = queryStringConstraints;
translatedRoute.CultureName = cultureName;
translatedRoute.DataTokens.Add("cultureName", cultureName);
@@ -476,7 +484,7 @@ private string CreateRouteUrl(string routeUrl, string routePrefix, string areaUr
private static string DetokenizeUrl(string url)
{
- return DetokenizeUrlRegex.Value.Replace(url, "");
+ return DetokenizeUrlRegex.Replace(url, "");
}
private static IEnumerable GetUrlParameterContents(string url)
From 5f2833a97d51e49df8a63d570f1bc37a496902a7 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Fri, 8 Mar 2013 12:16:27 -0700
Subject: [PATCH 72/97] updated readme and assembly info for 3.5.2
---
README.textile | 4 ++++
src/SharedAssemblyInfo.cs | 6 +++---
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/README.textile b/README.textile
index bf02d57..ff7db76 100644
--- a/README.textile
+++ b/README.textile
@@ -4,6 +4,10 @@ h2. Please refer to "attributerouting.net":http://attributerouting.net/ for docu
h3. Changelog
+_3.5.2_
+
+* 214 - BUG FIX: fixed thread safety issues encountered when dealing with query string route constraints."
+
_3.5.1_
* #199 - when applying inbound http method constraint, now checking the unvalidated form/query collection.
diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs
index 494d17d..af6e550 100644
--- a/src/SharedAssemblyInfo.cs
+++ b/src/SharedAssemblyInfo.cs
@@ -7,7 +7,7 @@
[assembly: AssemblyProduct("AttributeRouting")]
[assembly: AssemblyCopyright("Copyright 2010-2013 Tim McCall")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("3.5.1")]
-[assembly: AssemblyFileVersion("3.5.1")]
-[assembly: AssemblyInformationalVersion("3.5.1")]
+[assembly: AssemblyVersion("3.5.2")]
+[assembly: AssemblyFileVersion("3.5.2")]
+[assembly: AssemblyInformationalVersion("3.5.2")]
[assembly: AssemblyConfiguration("Release")]
From 5267a439a6db955d0a64f16d4161ae5ff212444b Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Fri, 8 Mar 2013 15:54:06 -0700
Subject: [PATCH 73/97] put the query constraints back in the route debugger
output.
---
.../Program.cs | 2 +-
.../Logging/LoggingExtensions.cs | 29 ++--
.../Logging/LogRoutesHandler.cs | 7 +-
.../Logging/LoggingExtensions.cs | 19 ++-
src/AttributeRouting/AttributeRouting.csproj | 2 +-
.../Logging/AttributeRouteInfo.cs | 128 ----------------
src/AttributeRouting/Logging/LogWriter.cs | 2 +-
.../Logging/RouteLoggingInfo.cs | 142 ++++++++++++++++++
8 files changed, 181 insertions(+), 150 deletions(-)
delete mode 100644 src/AttributeRouting/Logging/AttributeRouteInfo.cs
create mode 100644 src/AttributeRouting/Logging/RouteLoggingInfo.cs
diff --git a/src/AttributeRouting.Tests.SelfHost/Program.cs b/src/AttributeRouting.Tests.SelfHost/Program.cs
index 128cf30..60409fd 100644
--- a/src/AttributeRouting.Tests.SelfHost/Program.cs
+++ b/src/AttributeRouting.Tests.SelfHost/Program.cs
@@ -23,7 +23,7 @@ static void Main(string[] args)
Console.WriteLine("Routes:");
- config.Routes.Cast().LogTo(Console.Out);
+ config.Routes.Cast().ToArray().LogTo(Console.Out);
Console.WriteLine("Routes:");
Console.WriteLine("Press Enter to quit.");
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Logging/LoggingExtensions.cs b/src/AttributeRouting.Web.Http.SelfHost/Logging/LoggingExtensions.cs
index 4087659..5e6a3e9 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Logging/LoggingExtensions.cs
+++ b/src/AttributeRouting.Web.Http.SelfHost/Logging/LoggingExtensions.cs
@@ -1,29 +1,34 @@
-using System.Collections.Generic;
-using System.IO;
+using System.IO;
using System.Linq;
-using System.Web.Http.Routing;
-using AttributeRouting.Framework;
+using System.Web.Http.Routing;
+using AttributeRouting.Framework;
+using AttributeRouting.Helpers;
using AttributeRouting.Logging;
namespace AttributeRouting.Web.Http.SelfHost.Logging
{
public static class LoggingExtensions
{
- public static void LogTo(this IEnumerable routes, TextWriter writer)
+ public static void LogTo(this HttpRoute[] routes, TextWriter writer)
{
LogWriter.LogNumberOfRoutes(routes.Count(), writer);
- foreach (var route in routes)
- route.LogTo(writer);
+ foreach (var route in routes)
+ {
+ route.LogTo(writer);
+ }
}
public static void LogTo(this HttpRoute route, TextWriter writer)
{
- string name = route is IAttributeRoute
- ? ((IAttributeRoute)route).RouteName
- : null;
-
- LogWriter.LogRoute(writer, route.RouteTemplate, AttributeRouteInfo.GetRouteInfo(route.RouteTemplate, route.Defaults, route.Constraints, route.DataTokens));
+ var attributeRoute = route as IAttributeRoute;
+ var info = RouteLoggingInfo.GetRouteInfo(route.RouteTemplate,
+ route.Defaults,
+ route.Constraints,
+ attributeRoute.SafeGet(x => x.QueryStringConstraints),
+ route.DataTokens);
+
+ LogWriter.LogRoute(writer, route.RouteTemplate, info);
}
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web/Logging/LogRoutesHandler.cs b/src/AttributeRouting.Web/Logging/LogRoutesHandler.cs
index 6284863..a630758 100644
--- a/src/AttributeRouting.Web/Logging/LogRoutesHandler.cs
+++ b/src/AttributeRouting.Web/Logging/LogRoutesHandler.cs
@@ -96,7 +96,12 @@ private string GetOutput()
private static IEnumerable GetRouteInfo()
{
return from r in RouteTable.Routes.OfType()
- let routeInfo = AttributeRouteInfo.GetRouteInfo(r.Url, r.Defaults, r.Constraints, r.DataTokens)
+ let ar = r as IAttributeRoute
+ let routeInfo = RouteLoggingInfo.GetRouteInfo(r.Url,
+ r.Defaults,
+ r.Constraints,
+ ar.SafeGet(x => x.QueryStringConstraints),
+ r.DataTokens)
select new
{
methods = routeInfo.HttpMethods,
diff --git a/src/AttributeRouting.Web/Logging/LoggingExtensions.cs b/src/AttributeRouting.Web/Logging/LoggingExtensions.cs
index 96acb9f..a6e1ad9 100644
--- a/src/AttributeRouting.Web/Logging/LoggingExtensions.cs
+++ b/src/AttributeRouting.Web/Logging/LoggingExtensions.cs
@@ -2,7 +2,8 @@
using System.IO;
using System.Linq;
using System.Web.Routing;
-using AttributeRouting.Framework;
+using AttributeRouting.Framework;
+using AttributeRouting.Helpers;
using AttributeRouting.Logging;
namespace AttributeRouting.Web.Logging
@@ -21,11 +22,17 @@ public static void LogTo(this IEnumerable routes, TextWriter writer)
}
}
- public static void LogTo(this Route route, TextWriter writer)
- {
- var name = route is IAttributeRoute ? ((IAttributeRoute)route).RouteName : null;
-
- LogWriter.LogRoute(writer, name, AttributeRouteInfo.GetRouteInfo(route.Url, route.Defaults, route.Constraints, route.DataTokens));
+ public static void LogTo(this Route route, TextWriter writer)
+ {
+ var attributeRoute = route as IAttributeRoute;
+ var name = attributeRoute.SafeGet(r => r.RouteName);
+ var info = RouteLoggingInfo.GetRouteInfo(route.Url,
+ route.Defaults,
+ route.Constraints,
+ attributeRoute.SafeGet(r => r.QueryStringConstraints),
+ route.DataTokens);
+
+ LogWriter.LogRoute(writer, name, info);
}
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting/AttributeRouting.csproj b/src/AttributeRouting/AttributeRouting.csproj
index 2b7e3ef..e666f46 100644
--- a/src/AttributeRouting/AttributeRouting.csproj
+++ b/src/AttributeRouting/AttributeRouting.csproj
@@ -105,7 +105,7 @@
-
+
diff --git a/src/AttributeRouting/Logging/AttributeRouteInfo.cs b/src/AttributeRouting/Logging/AttributeRouteInfo.cs
deleted file mode 100644
index 5be63b6..0000000
--- a/src/AttributeRouting/Logging/AttributeRouteInfo.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using AttributeRouting.Constraints;
-using AttributeRouting.Helpers;
-
-namespace AttributeRouting.Logging
-{
- public class AttributeRouteInfo
- {
- public AttributeRouteInfo()
- {
- Defaults = new Dictionary();
- Constraints = new Dictionary();
- DataTokens = new Dictionary();
- }
-
- public string Url { get; set; }
- public string HttpMethods { get; set; }
- public IDictionary Defaults { get; set; }
- public IDictionary Constraints { get; set; }
- public IDictionary DataTokens { get; set; }
-
- public static AttributeRouteInfo GetRouteInfo(string url,
- IDictionary defaults,
- IDictionary constraints,
- IDictionary dataTokens)
- {
-
- var item = new AttributeRouteInfo { Url = url };
-
- //************************
- // Defaults
-
- if (defaults != null)
- {
- foreach (var @default in defaults)
- {
- var defaultValue = @default.Value.ToString();
- item.Defaults.Add(@default.Key, defaultValue.ValueOr("Optional"));
- }
- }
-
- //************************
- // Constraints
-
- if (constraints != null)
- {
- foreach (var constraint in constraints)
- {
- if (constraint.Value == null || constraint.Value is IInboundHttpMethodConstraint)
- continue;
-
- if (constraint.Value is RegexRouteConstraintBase)
- {
- item.Constraints.Add(constraint.Key, ((RegexRouteConstraintBase)constraint.Value).Pattern);
- }
- else
- {
- var constraintValue = constraint.Value;
- var constraintDescriptions = new List();
-
- // Simple string regex constraint - from ASP.NET routing features
- if (constraintValue is string)
- {
- constraintDescriptions.Add(constraintValue.ToString());
- }
- else
- {
- // Optional constraint - unwrap it and continue
- var optionalConstraint = constraintValue as IOptionalRouteConstraint;
- if (optionalConstraint != null)
- {
- constraintValue = optionalConstraint.Constraint;
- }
-
- // QueryString constraint - unwrap it and continue
- var queryStringConstraint = constraintValue as IQueryStringRouteConstraint;
- if (queryStringConstraint != null)
- {
- constraintValue = queryStringConstraint.Constraint;
- constraintDescriptions.Add("QueryStringRouteConstraint");
- }
-
- // Compound constraint - join type names of the inner constraints
- var compoundConstraint = constraintValue as ICompoundRouteConstraint;
- if (compoundConstraint != null)
- {
- constraintDescriptions.AddRange(compoundConstraint.Constraints.Select(c => c.GetType().Name));
- }
- else if (constraintValue != null)
- {
- // Single constraint type
- constraintDescriptions.Add(constraintValue.GetType().Name);
- }
- }
-
- item.Constraints.Add(constraint.Key, String.Join(", ", constraintDescriptions));
- }
- }
- }
-
- //************************
- // DataTokens
-
- if (dataTokens != null)
- {
- foreach (var token in dataTokens)
- {
- if (token.Key.ValueEquals("namespaces"))
- {
- item.DataTokens.Add(token.Key, String.Join(", ", (string[])token.Value));
- }
- else if (token.Key.ValueEquals("httpMethods"))
- {
- item.HttpMethods = String.Join(", ", (string[])token.Value);
- }
- else if (!token.Key.ValueEquals("actionMethod"))
- {
- item.DataTokens.Add(token.Key, token.Value.ToString());
- }
- }
- }
-
- return item;
- }
- }
-}
\ No newline at end of file
diff --git a/src/AttributeRouting/Logging/LogWriter.cs b/src/AttributeRouting/Logging/LogWriter.cs
index c2ef733..c08dc78 100644
--- a/src/AttributeRouting/Logging/LogWriter.cs
+++ b/src/AttributeRouting/Logging/LogWriter.cs
@@ -13,7 +13,7 @@ public static void LogNumberOfRoutes(int count, TextWriter writer)
writer.WriteLine(" ");
}
- public static void LogRoute(TextWriter writer, string name, AttributeRouteInfo routeInfo)
+ public static void LogRoute(TextWriter writer, string name, RouteLoggingInfo routeInfo)
{
writer.WriteLine("URL: {0} {1}", routeInfo.Url, routeInfo.HttpMethods);
diff --git a/src/AttributeRouting/Logging/RouteLoggingInfo.cs b/src/AttributeRouting/Logging/RouteLoggingInfo.cs
new file mode 100644
index 0000000..dc3ad61
--- /dev/null
+++ b/src/AttributeRouting/Logging/RouteLoggingInfo.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using AttributeRouting.Constraints;
+using AttributeRouting.Helpers;
+
+namespace AttributeRouting.Logging
+{
+ public class RouteLoggingInfo
+ {
+ public RouteLoggingInfo()
+ {
+ Defaults = new Dictionary();
+ Constraints = new Dictionary();
+ DataTokens = new Dictionary();
+ }
+
+ public string Url { get; set; }
+ public string HttpMethods { get; set; }
+ public IDictionary Defaults { get; set; }
+ public IDictionary Constraints { get; set; }
+ public IDictionary DataTokens { get; set; }
+
+ public static RouteLoggingInfo GetRouteInfo(string url,
+ IDictionary defaults,
+ IDictionary constraints,
+ IDictionary queryStringConstraints,
+ IDictionary dataTokens)
+ {
+
+ var item = new RouteLoggingInfo { Url = url };
+
+ //************************
+ // Defaults
+
+ if (defaults != null)
+ {
+ foreach (var @default in defaults)
+ {
+ var defaultValue = @default.Value.ToString();
+ item.Defaults.Add(@default.Key, defaultValue.ValueOr("Optional"));
+ }
+ }
+
+ //************************
+ // Constraints
+
+ var allConstraints = new Dictionary();
+ if (constraints != null)
+ {
+ foreach (var constraint in constraints)
+ {
+ allConstraints.Add(constraint.Key, constraint.Value);
+ }
+ }
+ if (queryStringConstraints != null)
+ {
+ foreach (var constraint in queryStringConstraints)
+ {
+ allConstraints.Add(constraint.Key, constraint.Value);
+ }
+ }
+
+ foreach (var constraint in allConstraints)
+ {
+ if (constraint.Value == null || constraint.Value is IInboundHttpMethodConstraint)
+ continue;
+
+ if (constraint.Value is RegexRouteConstraintBase)
+ {
+ item.Constraints.Add(constraint.Key, ((RegexRouteConstraintBase)constraint.Value).Pattern);
+ }
+ else
+ {
+ var constraintValue = constraint.Value;
+ var constraintDescriptions = new List();
+
+ // Simple string regex constraint - from ASP.NET routing features
+ if (constraintValue is string)
+ {
+ constraintDescriptions.Add(constraintValue.ToString());
+ }
+ else
+ {
+ // Optional constraint - unwrap it and continue
+ var optionalConstraint = constraintValue as IOptionalRouteConstraint;
+ if (optionalConstraint != null)
+ {
+ constraintValue = optionalConstraint.Constraint;
+ }
+
+ // QueryString constraint - unwrap it and continue
+ var queryStringConstraint = constraintValue as IQueryStringRouteConstraint;
+ if (queryStringConstraint != null)
+ {
+ constraintValue = queryStringConstraint.Constraint;
+ constraintDescriptions.Add("QueryStringRouteConstraint");
+ }
+
+ // Compound constraint - join type names of the inner constraints
+ var compoundConstraint = constraintValue as ICompoundRouteConstraint;
+ if (compoundConstraint != null)
+ {
+ constraintDescriptions.AddRange(compoundConstraint.Constraints.Select(c => c.GetType().Name));
+ }
+ else if (constraintValue != null)
+ {
+ // Single constraint type
+ constraintDescriptions.Add(constraintValue.GetType().Name);
+ }
+ }
+
+ item.Constraints.Add(constraint.Key, String.Join(", ", constraintDescriptions));
+ }
+ }
+
+ //************************
+ // DataTokens
+
+ if (dataTokens != null)
+ {
+ foreach (var token in dataTokens)
+ {
+ if (token.Key.ValueEquals("namespaces"))
+ {
+ item.DataTokens.Add(token.Key, String.Join(", ", (string[])token.Value));
+ }
+ else if (token.Key.ValueEquals("httpMethods"))
+ {
+ item.HttpMethods = String.Join(", ", (string[])token.Value);
+ }
+ else if (!token.Key.ValueEquals("actionMethod"))
+ {
+ item.DataTokens.Add(token.Key, token.Value.ToString());
+ }
+ }
+ }
+
+ return item;
+ }
+ }
+}
\ No newline at end of file
From 00e50861420b6517c9d9ef35df624b162c865a95 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Fri, 8 Mar 2013 16:45:27 -0700
Subject: [PATCH 74/97] #216 #211 - testing optional constraint for mvc against
null/empty in addition to UrlParameter.Optional; parsing defaults from
complete url, including areas and prefixes.
---
.../Features/RouteDefaults.feature | 1 +
.../Features/RouteDefaults.feature.cs | 2 +-
.../Steps/RouteDefaultsSteps.cs | 1 -
.../Steps/StandardUsageSteps.cs | 12 +++++++++---
.../Subjects/RouteDefaultsController.cs | 6 ++++++
.../Constraints/OptionalRouteConstraint.cs | 12 +++++++-----
.../Framework/RouteBuilder.cs | 19 +++++++++----------
.../Logging/RouteLoggingInfo.cs | 5 +++--
8 files changed, 36 insertions(+), 22 deletions(-)
diff --git a/src/AttributeRouting.Specs/Features/RouteDefaults.feature b/src/AttributeRouting.Specs/Features/RouteDefaults.feature
index be34fe1..2166809 100644
--- a/src/AttributeRouting.Specs/Features/RouteDefaults.feature
+++ b/src/AttributeRouting.Specs/Features/RouteDefaults.feature
@@ -29,3 +29,4 @@ Scenario: Using the controller and action url params
Then the route url is "RouteDefaults/TheActionName"
When I fetch the routes for the HttpRouteDefaults controller's TheActionName action
Then the route url is "HttpRouteDefaults/TheActionName"
+
diff --git a/src/AttributeRouting.Specs/Features/RouteDefaults.feature.cs b/src/AttributeRouting.Specs/Features/RouteDefaults.feature.cs
index e7552b2..66688d1 100644
--- a/src/AttributeRouting.Specs/Features/RouteDefaults.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteDefaults.feature.cs
@@ -3,7 +3,7 @@
// This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:1.9.0.77
// SpecFlow Generator Version:1.9.0.0
-// Runtime Version:4.0.30319.18010
+// Runtime Version:4.0.30319.18034
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
diff --git a/src/AttributeRouting.Specs/Steps/RouteDefaultsSteps.cs b/src/AttributeRouting.Specs/Steps/RouteDefaultsSteps.cs
index ce507c1..452558d 100644
--- a/src/AttributeRouting.Specs/Steps/RouteDefaultsSteps.cs
+++ b/src/AttributeRouting.Specs/Steps/RouteDefaultsSteps.cs
@@ -2,7 +2,6 @@
using System.Web.Http;
using System.Web.Mvc;
using AttributeRouting.Framework;
-using AttributeRouting.Web.Mvc.Framework;
using NUnit.Framework;
using TechTalk.SpecFlow;
diff --git a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
index c68d4d4..decf0df 100644
--- a/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
+++ b/src/AttributeRouting.Specs/Steps/StandardUsageSteps.cs
@@ -1,10 +1,7 @@
using System;
-using System.Collections.Generic;
using System.Linq;
using System.Web.Routing;
using AttributeRouting.Constraints;
-using AttributeRouting.Framework;
-using AttributeRouting.Web.Constraints;
using NUnit.Framework;
using TechTalk.SpecFlow;
@@ -36,6 +33,15 @@ public void ThenTheDefaultForIs(string key, object value)
Assert.That(route.Defaults[key], Is.EqualTo(value));
}
+ [Then(@"a default for ""(.*?)"" does not exist")]
+ public void ThenTheDefaultForDoesNotExist(string key)
+ {
+ var route = ScenarioContext.Current.GetFetchedRoutes().FirstOrDefault();
+
+ Assert.That(route, Is.Not.Null);
+ Assert.That(route.Defaults[key], Is.Null);
+ }
+
[Then(@"the route area is ""(.*?)""")]
public void ThenTheRouteAreaIs(string area)
{
diff --git a/src/AttributeRouting.Specs/Subjects/RouteDefaultsController.cs b/src/AttributeRouting.Specs/Subjects/RouteDefaultsController.cs
index e79638e..cdefd87 100644
--- a/src/AttributeRouting.Specs/Subjects/RouteDefaultsController.cs
+++ b/src/AttributeRouting.Specs/Subjects/RouteDefaultsController.cs
@@ -22,5 +22,11 @@ public string TheActionName()
{
return "is joe";
}
+
+ [GET("Optionals/In/Query?{x?}")]
+ public string OptionalsInQuery()
+ {
+ return "optionals in query";
+ }
}
}
\ No newline at end of file
diff --git a/src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraint.cs b/src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraint.cs
index 95697ac..e6fc342 100644
--- a/src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraint.cs
+++ b/src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraint.cs
@@ -1,8 +1,9 @@
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
-using AttributeRouting.Constraints;
-
+using AttributeRouting.Constraints;
+using AttributeRouting.Helpers;
+
namespace AttributeRouting.Web.Mvc.Constraints
{
public class OptionalRouteConstraint : IOptionalRouteConstraint, IRouteConstraint
@@ -23,9 +24,10 @@ public bool Match(HttpContextBase httpContext, Route route, string parameterName
{
// If the param is optional and has no value, then pass the constraint
if (route.Defaults.ContainsKey(parameterName)
- && route.Defaults[parameterName] == UrlParameter.Optional)
- {
- if (values[parameterName] == UrlParameter.Optional)
+ && route.Defaults[parameterName] == UrlParameter.Optional)
+ {
+ var value = values[parameterName];
+ if (value == UrlParameter.Optional || value.HasNoValue())
return true;
}
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index ca8371b..31dbe2b 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -144,9 +144,7 @@ private void CreateRouteConstraints(RouteSpecification routeSpec, out IDictionar
var tokenizedUrl = BuildTokenizedUrl(routeSpec.RouteUrl, routeSpec.RoutePrefixUrl, routeSpec.AreaUrl, routeSpec);
var urlParameters = GetUrlParameterContents(tokenizedUrl).ToList();
- // Need to keep track of query params.
- // Can do this by detokenizing URL (which strips query),
- // and then taking all the URL parameters except those from the path part of the URL.
+ // Need to keep track of which are path and query params.
var pathOnlyUrl = RemoveQueryString(tokenizedUrl);
var pathOnlyUrlParameters = GetUrlParameterContents(pathOnlyUrl);
var queryStringParameters = urlParameters.Except(pathOnlyUrlParameters).ToList();
@@ -299,11 +297,13 @@ private IDictionary CreateRouteDefaults(RouteSpecification route
{
{ "controller", routeSpec.ControllerName },
{ "action", routeSpec.ActionName }
- };
-
- var urlParameters = GetUrlParameterContents(routeSpec.RouteUrl).ToList();
-
- // Inspect the url for optional parameters, specified with a trailing ?
+ };
+
+ // Work from a complete, tokenized url; ie: support defaults in area urls, route prefix urls, and route urls.
+ var tokenizedUrl = BuildTokenizedUrl(routeSpec.RouteUrl, routeSpec.RoutePrefixUrl, routeSpec.AreaUrl, routeSpec);
+ var urlParameters = GetUrlParameterContents(tokenizedUrl).ToList();
+
+ // Inspect the url path for optional parameters, specified with a trailing ?
foreach (var parameter in urlParameters.Where(p => p.EndsWith("?")))
{
var parameterName = parameter.TrimEnd('?');
@@ -430,11 +430,10 @@ private string CreateRouteUrl(IDictionary defaults, RouteSpecifi
}
private string CreateRouteUrl(string routeUrl, string routePrefix, string areaUrl, IDictionary defaults, RouteSpecification routeSpec)
- {
+ {
var tokenizedUrl = BuildTokenizedUrl(routeUrl, routePrefix, areaUrl, routeSpec);
var tokenizedPath = RemoveQueryString(tokenizedUrl);
var detokenizedPath = DetokenizeUrl(tokenizedPath);
-
var urlParameterNames = GetUrlParameterContents(detokenizedPath).ToList();
var urlBuilder = new StringBuilder(detokenizedPath);
diff --git a/src/AttributeRouting/Logging/RouteLoggingInfo.cs b/src/AttributeRouting/Logging/RouteLoggingInfo.cs
index dc3ad61..aa2d93a 100644
--- a/src/AttributeRouting/Logging/RouteLoggingInfo.cs
+++ b/src/AttributeRouting/Logging/RouteLoggingInfo.cs
@@ -66,9 +66,10 @@ public static RouteLoggingInfo GetRouteInfo(string url,
if (constraint.Value == null || constraint.Value is IInboundHttpMethodConstraint)
continue;
- if (constraint.Value is RegexRouteConstraintBase)
+ var regexRouteConstraint = constraint.Value as RegexRouteConstraintBase;
+ if (regexRouteConstraint != null)
{
- item.Constraints.Add(constraint.Key, ((RegexRouteConstraintBase)constraint.Value).Pattern);
+ item.Constraints.Add(constraint.Key, regexRouteConstraint.Pattern);
}
else
{
From 8f0f7951fe758768f00a83edff130cd733dbd376 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Fri, 8 Mar 2013 16:48:02 -0700
Subject: [PATCH 75/97] updated readme and assembly info for 2.5.3
---
README.textile | 7 ++++++-
src/SharedAssemblyInfo.cs | 6 +++---
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/README.textile b/README.textile
index ff7db76..ec79586 100644
--- a/README.textile
+++ b/README.textile
@@ -4,9 +4,14 @@ h2. Please refer to "attributerouting.net":http://attributerouting.net/ for docu
h3. Changelog
+_3.5.3_
+
+* #216 - testing optional constraint for mvc against null/empty in addition to UrlParameter.Optional.
+* #211 - parsing defaults from complete url, including areas and prefixes.
+
_3.5.2_
-* 214 - BUG FIX: fixed thread safety issues encountered when dealing with query string route constraints."
+* #214 - BUG FIX: fixed thread safety issues encountered when dealing with query string route constraints."
_3.5.1_
diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs
index af6e550..2e2e156 100644
--- a/src/SharedAssemblyInfo.cs
+++ b/src/SharedAssemblyInfo.cs
@@ -7,7 +7,7 @@
[assembly: AssemblyProduct("AttributeRouting")]
[assembly: AssemblyCopyright("Copyright 2010-2013 Tim McCall")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("3.5.2")]
-[assembly: AssemblyFileVersion("3.5.2")]
-[assembly: AssemblyInformationalVersion("3.5.2")]
+[assembly: AssemblyVersion("2.5.3")]
+[assembly: AssemblyFileVersion("2.5.3")]
+[assembly: AssemblyInformationalVersion("2.5.3")]
[assembly: AssemblyConfiguration("Release")]
From 73d2cd89d5a6d6235a153345ff74aaf459b2c3f4 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Fri, 8 Mar 2013 16:49:54 -0700
Subject: [PATCH 76/97] fixed assembly info version: end of day/week confusion
with numbers :)
---
src/SharedAssemblyInfo.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs
index 2e2e156..f506102 100644
--- a/src/SharedAssemblyInfo.cs
+++ b/src/SharedAssemblyInfo.cs
@@ -7,7 +7,7 @@
[assembly: AssemblyProduct("AttributeRouting")]
[assembly: AssemblyCopyright("Copyright 2010-2013 Tim McCall")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("2.5.3")]
-[assembly: AssemblyFileVersion("2.5.3")]
-[assembly: AssemblyInformationalVersion("2.5.3")]
+[assembly: AssemblyVersion("3.5.3")]
+[assembly: AssemblyFileVersion("3.5.3")]
+[assembly: AssemblyInformationalVersion("3.5.3")]
[assembly: AssemblyConfiguration("Release")]
From 2cbef22058735061b28e96e3ada7535502b82b4c Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sun, 10 Mar 2013 12:16:07 -0600
Subject: [PATCH 77/97] Started refactoring tests by writing some jasmine
integration tests.
---
.../AttributeRouting.Tests.csproj | 205 ++
src/AttributeRouting.Tests/Global.asax | 1 +
src/AttributeRouting.Tests/Global.asax.cs | 18 +
.../Integration/css/jasmine.css | 82 +
.../Integration/css/runner.css | 9 +
.../Integration/js/lib/jasmine-html.js | 681 +++++
.../Integration/js/lib/jasmine.js | 2600 +++++++++++++++++
.../Integration/js/lib/jquery-1.9.1.min.js | 5 +
.../Integration/js/specs/basic-usage.js | 16 +
.../Integration/js/specs/specs.js | 46 +
.../Integration/runner.html | 47 +
.../Properties/AssemblyInfo.cs | 35 +
.../Subjects/BasicUsageController.cs | 34 +
.../Subjects/HttpBasicUsageController.cs | 34 +
src/AttributeRouting.Tests/Web.Debug.config | 30 +
src/AttributeRouting.Tests/Web.Release.config | 31 +
src/AttributeRouting.Tests/Web.config | 48 +
src/AttributeRouting.Tests/packages.config | 16 +
src/AttributeRouting.sln | 13 +
19 files changed, 3951 insertions(+)
create mode 100644 src/AttributeRouting.Tests/AttributeRouting.Tests.csproj
create mode 100644 src/AttributeRouting.Tests/Global.asax
create mode 100644 src/AttributeRouting.Tests/Global.asax.cs
create mode 100644 src/AttributeRouting.Tests/Integration/css/jasmine.css
create mode 100644 src/AttributeRouting.Tests/Integration/css/runner.css
create mode 100644 src/AttributeRouting.Tests/Integration/js/lib/jasmine-html.js
create mode 100644 src/AttributeRouting.Tests/Integration/js/lib/jasmine.js
create mode 100644 src/AttributeRouting.Tests/Integration/js/lib/jquery-1.9.1.min.js
create mode 100644 src/AttributeRouting.Tests/Integration/js/specs/basic-usage.js
create mode 100644 src/AttributeRouting.Tests/Integration/js/specs/specs.js
create mode 100644 src/AttributeRouting.Tests/Integration/runner.html
create mode 100644 src/AttributeRouting.Tests/Properties/AssemblyInfo.cs
create mode 100644 src/AttributeRouting.Tests/Subjects/BasicUsageController.cs
create mode 100644 src/AttributeRouting.Tests/Subjects/HttpBasicUsageController.cs
create mode 100644 src/AttributeRouting.Tests/Web.Debug.config
create mode 100644 src/AttributeRouting.Tests/Web.Release.config
create mode 100644 src/AttributeRouting.Tests/Web.config
create mode 100644 src/AttributeRouting.Tests/packages.config
diff --git a/src/AttributeRouting.Tests/AttributeRouting.Tests.csproj b/src/AttributeRouting.Tests/AttributeRouting.Tests.csproj
new file mode 100644
index 0000000..f128f7d
--- /dev/null
+++ b/src/AttributeRouting.Tests/AttributeRouting.Tests.csproj
@@ -0,0 +1,205 @@
+
+
+
+
+ Debug
+ AnyCPU
+
+
+ 2.0
+ {2A6712B2-0912-457D-A66E-982640E9F411}
+ {E3E379DF-F4C6-4180-9B81-6769533ABE47};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
+ Library
+ Properties
+ AttributeRouting.Tests
+ AttributeRouting.Tests
+ v4.5
+ false
+ true
+
+
+
+
+ ..\
+ true
+
+
+ true
+ full
+ false
+ bin\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll
+
+
+ True
+ ..\packages\Microsoft.AspNet.Mvc.FixedDisplayModes.1.0.0\lib\net40\Microsoft.Web.Mvc.FixedDisplayModes.dll
+
+
+ ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll
+
+
+
+
+ ..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll
+
+
+
+
+ True
+ ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll
+
+
+ ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll
+
+
+ ..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.20710.0\lib\net40\System.Web.Http.WebHost.dll
+
+
+ True
+ ..\packages\Microsoft.AspNet.Mvc.4.0.20710.0\lib\net40\System.Web.Mvc.dll
+
+
+ True
+ ..\packages\Microsoft.AspNet.Razor.2.0.20715.0\lib\net40\System.Web.Razor.dll
+
+
+ True
+ ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll
+
+
+ True
+ ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll
+
+
+ True
+ ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll
+
+
+
+
+ Global.asax
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Web.config
+
+
+ Web.config
+
+
+
+
+
+
+
+
+
+
+
+ {246f7aec-9429-4fbb-8747-92a3f025c711}
+ AttributeRouting.Web.Http.SelfHost
+
+
+ {a018fec5-45f8-44fb-bb6c-33697b418434}
+ AttributeRouting.Web.Http.WebHost
+
+
+ {ccde9ad7-3822-4b0b-aa19-df6698a85d3d}
+ AttributeRouting.Web.Http
+
+
+ {4604c450-ebf8-4a7f-bd3a-a24655c41fa4}
+ AttributeRouting.Web.Mvc
+
+
+ {c91c065b-a821-4890-9f31-f9e245d804d1}
+ AttributeRouting.Web
+
+
+ {871a79cf-c705-4c6b-8938-f9aa1e02aea4}
+ AttributeRouting
+
+
+
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ 49551
+ /
+ http://localhost:49540/
+ False
+ False
+
+
+ False
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Global.asax b/src/AttributeRouting.Tests/Global.asax
new file mode 100644
index 0000000..4da0999
--- /dev/null
+++ b/src/AttributeRouting.Tests/Global.asax
@@ -0,0 +1 @@
+<%@ Application Codebehind="Global.asax.cs" Inherits="AttributeRouting.Tests.MvcApplication" Language="C#" %>
diff --git a/src/AttributeRouting.Tests/Global.asax.cs b/src/AttributeRouting.Tests/Global.asax.cs
new file mode 100644
index 0000000..d5b54c4
--- /dev/null
+++ b/src/AttributeRouting.Tests/Global.asax.cs
@@ -0,0 +1,18 @@
+using System.Web.Http;
+using System.Web.Routing;
+using AttributeRouting.Web.Http.WebHost;
+using AttributeRouting.Web.Mvc;
+
+namespace AttributeRouting.Tests
+{
+ // Note: For instructions on enabling IIS6 or IIS7 classic mode,
+ // visit http://go.microsoft.com/?LinkId=9394801
+ public class MvcApplication : System.Web.HttpApplication
+ {
+ protected void Application_Start()
+ {
+ RouteTable.Routes.MapAttributeRoutes();
+ GlobalConfiguration.Configuration.Routes.MapHttpAttributeRoutes();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Integration/css/jasmine.css b/src/AttributeRouting.Tests/Integration/css/jasmine.css
new file mode 100644
index 0000000..8c008dc
--- /dev/null
+++ b/src/AttributeRouting.Tests/Integration/css/jasmine.css
@@ -0,0 +1,82 @@
+body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
+
+#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
+#HTMLReporter a { text-decoration: none; }
+#HTMLReporter a:hover { text-decoration: underline; }
+#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
+#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
+#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
+#HTMLReporter .version { color: #aaaaaa; }
+#HTMLReporter .banner { margin-top: 14px; }
+#HTMLReporter .duration { color: #aaaaaa; float: right; }
+#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
+#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
+#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
+#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
+#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
+#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
+#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
+#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
+#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
+#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
+#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
+#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
+#HTMLReporter .runningAlert { background-color: #666666; }
+#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
+#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
+#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
+#HTMLReporter .passingAlert { background-color: #a6b779; }
+#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
+#HTMLReporter .failingAlert { background-color: #cf867e; }
+#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
+#HTMLReporter .results { margin-top: 14px; }
+#HTMLReporter #details { display: none; }
+#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
+#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
+#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
+#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
+#HTMLReporter.showDetails .summary { display: none; }
+#HTMLReporter.showDetails #details { display: block; }
+#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
+#HTMLReporter .summary { margin-top: 14px; }
+#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
+#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
+#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
+#HTMLReporter .description + .suite { margin-top: 0; }
+#HTMLReporter .suite { margin-top: 14px; }
+#HTMLReporter .suite a { color: #333333; }
+#HTMLReporter #details .specDetail { margin-bottom: 28px; }
+#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
+#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
+#HTMLReporter .resultMessage span.result { display: block; }
+#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
+
+#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
+#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
+#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
+#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
+#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
+#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
+#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
+#TrivialReporter .runner.running { background-color: yellow; }
+#TrivialReporter .options { text-align: right; font-size: .8em; }
+#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
+#TrivialReporter .suite .suite { margin: 5px; }
+#TrivialReporter .suite.passed { background-color: #dfd; }
+#TrivialReporter .suite.failed { background-color: #fdd; }
+#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
+#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
+#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
+#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
+#TrivialReporter .spec.skipped { background-color: #bbb; }
+#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
+#TrivialReporter .passed { background-color: #cfc; display: none; }
+#TrivialReporter .failed { background-color: #fbb; }
+#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
+#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
+#TrivialReporter .resultMessage .mismatch { color: black; }
+#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
+#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
+#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
+#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
+#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
diff --git a/src/AttributeRouting.Tests/Integration/css/runner.css b/src/AttributeRouting.Tests/Integration/css/runner.css
new file mode 100644
index 0000000..08d5025
--- /dev/null
+++ b/src/AttributeRouting.Tests/Integration/css/runner.css
@@ -0,0 +1,9 @@
+.view-all-routes {
+ display: block;
+ padding: 5px;
+ font: 1em Helvetica, Arial, sans-serif;
+ background-color: #333;
+ color: white;
+ text-decoration: none;
+ text-align: center;
+}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Integration/js/lib/jasmine-html.js b/src/AttributeRouting.Tests/Integration/js/lib/jasmine-html.js
new file mode 100644
index 0000000..543d569
--- /dev/null
+++ b/src/AttributeRouting.Tests/Integration/js/lib/jasmine-html.js
@@ -0,0 +1,681 @@
+jasmine.HtmlReporterHelpers = {};
+
+jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
+ var el = document.createElement(type);
+
+ for (var i = 2; i < arguments.length; i++) {
+ var child = arguments[i];
+
+ if (typeof child === 'string') {
+ el.appendChild(document.createTextNode(child));
+ } else {
+ if (child) {
+ el.appendChild(child);
+ }
+ }
+ }
+
+ for (var attr in attrs) {
+ if (attr == "className") {
+ el[attr] = attrs[attr];
+ } else {
+ el.setAttribute(attr, attrs[attr]);
+ }
+ }
+
+ return el;
+};
+
+jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
+ var results = child.results();
+ var status = results.passed() ? 'passed' : 'failed';
+ if (results.skipped) {
+ status = 'skipped';
+ }
+
+ return status;
+};
+
+jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
+ var parentDiv = this.dom.summary;
+ var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
+ var parent = child[parentSuite];
+
+ if (parent) {
+ if (typeof this.views.suites[parent.id] == 'undefined') {
+ this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
+ }
+ parentDiv = this.views.suites[parent.id].element;
+ }
+
+ parentDiv.appendChild(childElement);
+};
+
+
+jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
+ for(var fn in jasmine.HtmlReporterHelpers) {
+ ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
+ }
+};
+
+jasmine.HtmlReporter = function(_doc) {
+ var self = this;
+ var doc = _doc || window.document;
+
+ var reporterView;
+
+ var dom = {};
+
+ // Jasmine Reporter Public Interface
+ self.logRunningSpecs = false;
+
+ self.reportRunnerStarting = function(runner) {
+ var specs = runner.specs() || [];
+
+ if (specs.length == 0) {
+ return;
+ }
+
+ createReporterDom(runner.env.versionString());
+ doc.body.appendChild(dom.reporter);
+ setExceptionHandling();
+
+ reporterView = new jasmine.HtmlReporter.ReporterView(dom);
+ reporterView.addSpecs(specs, self.specFilter);
+ };
+
+ self.reportRunnerResults = function(runner) {
+ reporterView && reporterView.complete();
+ };
+
+ self.reportSuiteResults = function(suite) {
+ reporterView.suiteComplete(suite);
+ };
+
+ self.reportSpecStarting = function(spec) {
+ if (self.logRunningSpecs) {
+ self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
+ }
+ };
+
+ self.reportSpecResults = function(spec) {
+ reporterView.specComplete(spec);
+ };
+
+ self.log = function() {
+ var console = jasmine.getGlobal().console;
+ if (console && console.log) {
+ if (console.log.apply) {
+ console.log.apply(console, arguments);
+ } else {
+ console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
+ }
+ }
+ };
+
+ self.specFilter = function(spec) {
+ if (!focusedSpecName()) {
+ return true;
+ }
+
+ return spec.getFullName().indexOf(focusedSpecName()) === 0;
+ };
+
+ return self;
+
+ function focusedSpecName() {
+ var specName;
+
+ (function memoizeFocusedSpec() {
+ if (specName) {
+ return;
+ }
+
+ var paramMap = [];
+ var params = jasmine.HtmlReporter.parameters(doc);
+
+ for (var i = 0; i < params.length; i++) {
+ var p = params[i].split('=');
+ paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
+ }
+
+ specName = paramMap.spec;
+ })();
+
+ return specName;
+ }
+
+ function createReporterDom(version) {
+ dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
+ dom.banner = self.createDom('div', { className: 'banner' },
+ self.createDom('span', { className: 'title' }, "Jasmine "),
+ self.createDom('span', { className: 'version' }, version)),
+
+ dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
+ dom.alert = self.createDom('div', {className: 'alert'},
+ self.createDom('span', { className: 'exceptions' },
+ self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'),
+ self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))),
+ dom.results = self.createDom('div', {className: 'results'},
+ dom.summary = self.createDom('div', { className: 'summary' }),
+ dom.details = self.createDom('div', { id: 'details' }))
+ );
+ }
+
+ function noTryCatch() {
+ return window.location.search.match(/catch=false/);
+ }
+
+ function searchWithCatch() {
+ var params = jasmine.HtmlReporter.parameters(window.document);
+ var removed = false;
+ var i = 0;
+
+ while (!removed && i < params.length) {
+ if (params[i].match(/catch=/)) {
+ params.splice(i, 1);
+ removed = true;
+ }
+ i++;
+ }
+ if (jasmine.CATCH_EXCEPTIONS) {
+ params.push("catch=false");
+ }
+
+ return params.join("&");
+ }
+
+ function setExceptionHandling() {
+ var chxCatch = document.getElementById('no_try_catch');
+
+ if (noTryCatch()) {
+ chxCatch.setAttribute('checked', true);
+ jasmine.CATCH_EXCEPTIONS = false;
+ }
+ chxCatch.onclick = function() {
+ window.location.search = searchWithCatch();
+ };
+ }
+};
+jasmine.HtmlReporter.parameters = function(doc) {
+ var paramStr = doc.location.search.substring(1);
+ var params = [];
+
+ if (paramStr.length > 0) {
+ params = paramStr.split('&');
+ }
+ return params;
+}
+jasmine.HtmlReporter.sectionLink = function(sectionName) {
+ var link = '?';
+ var params = [];
+
+ if (sectionName) {
+ params.push('spec=' + encodeURIComponent(sectionName));
+ }
+ if (!jasmine.CATCH_EXCEPTIONS) {
+ params.push("catch=false");
+ }
+ if (params.length > 0) {
+ link += params.join("&");
+ }
+
+ return link;
+};
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
+jasmine.HtmlReporter.ReporterView = function(dom) {
+ this.startedAt = new Date();
+ this.runningSpecCount = 0;
+ this.completeSpecCount = 0;
+ this.passedCount = 0;
+ this.failedCount = 0;
+ this.skippedCount = 0;
+
+ this.createResultsMenu = function() {
+ this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
+ this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
+ ' | ',
+ this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
+
+ this.summaryMenuItem.onclick = function() {
+ dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
+ };
+
+ this.detailsMenuItem.onclick = function() {
+ showDetails();
+ };
+ };
+
+ this.addSpecs = function(specs, specFilter) {
+ this.totalSpecCount = specs.length;
+
+ this.views = {
+ specs: {},
+ suites: {}
+ };
+
+ for (var i = 0; i < specs.length; i++) {
+ var spec = specs[i];
+ this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
+ if (specFilter(spec)) {
+ this.runningSpecCount++;
+ }
+ }
+ };
+
+ this.specComplete = function(spec) {
+ this.completeSpecCount++;
+
+ if (isUndefined(this.views.specs[spec.id])) {
+ this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
+ }
+
+ var specView = this.views.specs[spec.id];
+
+ switch (specView.status()) {
+ case 'passed':
+ this.passedCount++;
+ break;
+
+ case 'failed':
+ this.failedCount++;
+ break;
+
+ case 'skipped':
+ this.skippedCount++;
+ break;
+ }
+
+ specView.refresh();
+ this.refresh();
+ };
+
+ this.suiteComplete = function(suite) {
+ var suiteView = this.views.suites[suite.id];
+ if (isUndefined(suiteView)) {
+ return;
+ }
+ suiteView.refresh();
+ };
+
+ this.refresh = function() {
+
+ if (isUndefined(this.resultsMenu)) {
+ this.createResultsMenu();
+ }
+
+ // currently running UI
+ if (isUndefined(this.runningAlert)) {
+ this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" });
+ dom.alert.appendChild(this.runningAlert);
+ }
+ this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
+
+ // skipped specs UI
+ if (isUndefined(this.skippedAlert)) {
+ this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" });
+ }
+
+ this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
+
+ if (this.skippedCount === 1 && isDefined(dom.alert)) {
+ dom.alert.appendChild(this.skippedAlert);
+ }
+
+ // passing specs UI
+ if (isUndefined(this.passedAlert)) {
+ this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" });
+ }
+ this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
+
+ // failing specs UI
+ if (isUndefined(this.failedAlert)) {
+ this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
+ }
+ this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
+
+ if (this.failedCount === 1 && isDefined(dom.alert)) {
+ dom.alert.appendChild(this.failedAlert);
+ dom.alert.appendChild(this.resultsMenu);
+ }
+
+ // summary info
+ this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
+ this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
+ };
+
+ this.complete = function() {
+ dom.alert.removeChild(this.runningAlert);
+
+ this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
+
+ if (this.failedCount === 0) {
+ dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
+ } else {
+ showDetails();
+ }
+
+ dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
+ };
+
+ return this;
+
+ function showDetails() {
+ if (dom.reporter.className.search(/showDetails/) === -1) {
+ dom.reporter.className += " showDetails";
+ }
+ }
+
+ function isUndefined(obj) {
+ return typeof obj === 'undefined';
+ }
+
+ function isDefined(obj) {
+ return !isUndefined(obj);
+ }
+
+ function specPluralizedFor(count) {
+ var str = count + " spec";
+ if (count > 1) {
+ str += "s"
+ }
+ return str;
+ }
+
+};
+
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
+
+
+jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
+ this.spec = spec;
+ this.dom = dom;
+ this.views = views;
+
+ this.symbol = this.createDom('li', { className: 'pending' });
+ this.dom.symbolSummary.appendChild(this.symbol);
+
+ this.summary = this.createDom('div', { className: 'specSummary' },
+ this.createDom('a', {
+ className: 'description',
+ href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),
+ title: this.spec.getFullName()
+ }, this.spec.description)
+ );
+
+ this.detail = this.createDom('div', { className: 'specDetail' },
+ this.createDom('a', {
+ className: 'description',
+ href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
+ title: this.spec.getFullName()
+ }, this.spec.getFullName())
+ );
+};
+
+jasmine.HtmlReporter.SpecView.prototype.status = function() {
+ return this.getSpecStatus(this.spec);
+};
+
+jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
+ this.symbol.className = this.status();
+
+ switch (this.status()) {
+ case 'skipped':
+ break;
+
+ case 'passed':
+ this.appendSummaryToSuiteDiv();
+ break;
+
+ case 'failed':
+ this.appendSummaryToSuiteDiv();
+ this.appendFailureDetail();
+ break;
+ }
+};
+
+jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
+ this.summary.className += ' ' + this.status();
+ this.appendToSummary(this.spec, this.summary);
+};
+
+jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
+ this.detail.className += ' ' + this.status();
+
+ var resultItems = this.spec.results().getItems();
+ var messagesDiv = this.createDom('div', { className: 'messages' });
+
+ for (var i = 0; i < resultItems.length; i++) {
+ var result = resultItems[i];
+
+ if (result.type == 'log') {
+ messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
+ } else if (result.type == 'expect' && result.passed && !result.passed()) {
+ messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
+
+ if (result.trace.stack) {
+ messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
+ }
+ }
+ }
+
+ if (messagesDiv.childNodes.length > 0) {
+ this.detail.appendChild(messagesDiv);
+ this.dom.details.appendChild(this.detail);
+ }
+};
+
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
+ this.suite = suite;
+ this.dom = dom;
+ this.views = views;
+
+ this.element = this.createDom('div', { className: 'suite' },
+ this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)
+ );
+
+ this.appendToSummary(this.suite, this.element);
+};
+
+jasmine.HtmlReporter.SuiteView.prototype.status = function() {
+ return this.getSpecStatus(this.suite);
+};
+
+jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
+ this.element.className += " " + this.status();
+};
+
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
+
+/* @deprecated Use jasmine.HtmlReporter instead
+ */
+jasmine.TrivialReporter = function(doc) {
+ this.document = doc || document;
+ this.suiteDivs = {};
+ this.logRunningSpecs = false;
+};
+
+jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
+ var el = document.createElement(type);
+
+ for (var i = 2; i < arguments.length; i++) {
+ var child = arguments[i];
+
+ if (typeof child === 'string') {
+ el.appendChild(document.createTextNode(child));
+ } else {
+ if (child) { el.appendChild(child); }
+ }
+ }
+
+ for (var attr in attrs) {
+ if (attr == "className") {
+ el[attr] = attrs[attr];
+ } else {
+ el.setAttribute(attr, attrs[attr]);
+ }
+ }
+
+ return el;
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
+ var showPassed, showSkipped;
+
+ this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
+ this.createDom('div', { className: 'banner' },
+ this.createDom('div', { className: 'logo' },
+ this.createDom('span', { className: 'title' }, "Jasmine"),
+ this.createDom('span', { className: 'version' }, runner.env.versionString())),
+ this.createDom('div', { className: 'options' },
+ "Show ",
+ showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
+ this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
+ showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
+ this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
+ )
+ ),
+
+ this.runnerDiv = this.createDom('div', { className: 'runner running' },
+ this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
+ this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
+ this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
+ );
+
+ this.document.body.appendChild(this.outerDiv);
+
+ var suites = runner.suites();
+ for (var i = 0; i < suites.length; i++) {
+ var suite = suites[i];
+ var suiteDiv = this.createDom('div', { className: 'suite' },
+ this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
+ this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
+ this.suiteDivs[suite.id] = suiteDiv;
+ var parentDiv = this.outerDiv;
+ if (suite.parentSuite) {
+ parentDiv = this.suiteDivs[suite.parentSuite.id];
+ }
+ parentDiv.appendChild(suiteDiv);
+ }
+
+ this.startedAt = new Date();
+
+ var self = this;
+ showPassed.onclick = function(evt) {
+ if (showPassed.checked) {
+ self.outerDiv.className += ' show-passed';
+ } else {
+ self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
+ }
+ };
+
+ showSkipped.onclick = function(evt) {
+ if (showSkipped.checked) {
+ self.outerDiv.className += ' show-skipped';
+ } else {
+ self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
+ }
+ };
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
+ var results = runner.results();
+ var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
+ this.runnerDiv.setAttribute("class", className);
+ //do it twice for IE
+ this.runnerDiv.setAttribute("className", className);
+ var specs = runner.specs();
+ var specCount = 0;
+ for (var i = 0; i < specs.length; i++) {
+ if (this.specFilter(specs[i])) {
+ specCount++;
+ }
+ }
+ var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
+ message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
+ this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
+
+ this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
+};
+
+jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
+ var results = suite.results();
+ var status = results.passed() ? 'passed' : 'failed';
+ if (results.totalCount === 0) { // todo: change this to check results.skipped
+ status = 'skipped';
+ }
+ this.suiteDivs[suite.id].className += " " + status;
+};
+
+jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
+ if (this.logRunningSpecs) {
+ this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
+ }
+};
+
+jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
+ var results = spec.results();
+ var status = results.passed() ? 'passed' : 'failed';
+ if (results.skipped) {
+ status = 'skipped';
+ }
+ var specDiv = this.createDom('div', { className: 'spec ' + status },
+ this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
+ this.createDom('a', {
+ className: 'description',
+ href: '?spec=' + encodeURIComponent(spec.getFullName()),
+ title: spec.getFullName()
+ }, spec.description));
+
+
+ var resultItems = results.getItems();
+ var messagesDiv = this.createDom('div', { className: 'messages' });
+ for (var i = 0; i < resultItems.length; i++) {
+ var result = resultItems[i];
+
+ if (result.type == 'log') {
+ messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
+ } else if (result.type == 'expect' && result.passed && !result.passed()) {
+ messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
+
+ if (result.trace.stack) {
+ messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
+ }
+ }
+ }
+
+ if (messagesDiv.childNodes.length > 0) {
+ specDiv.appendChild(messagesDiv);
+ }
+
+ this.suiteDivs[spec.suite.id].appendChild(specDiv);
+};
+
+jasmine.TrivialReporter.prototype.log = function() {
+ var console = jasmine.getGlobal().console;
+ if (console && console.log) {
+ if (console.log.apply) {
+ console.log.apply(console, arguments);
+ } else {
+ console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
+ }
+ }
+};
+
+jasmine.TrivialReporter.prototype.getLocation = function() {
+ return this.document.location;
+};
+
+jasmine.TrivialReporter.prototype.specFilter = function(spec) {
+ var paramMap = {};
+ var params = this.getLocation().search.substring(1).split('&');
+ for (var i = 0; i < params.length; i++) {
+ var p = params[i].split('=');
+ paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
+ }
+
+ if (!paramMap.spec) {
+ return true;
+ }
+ return spec.getFullName().indexOf(paramMap.spec) === 0;
+};
diff --git a/src/AttributeRouting.Tests/Integration/js/lib/jasmine.js b/src/AttributeRouting.Tests/Integration/js/lib/jasmine.js
new file mode 100644
index 0000000..6b3459b
--- /dev/null
+++ b/src/AttributeRouting.Tests/Integration/js/lib/jasmine.js
@@ -0,0 +1,2600 @@
+var isCommonJS = typeof window == "undefined" && typeof exports == "object";
+
+/**
+ * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
+ *
+ * @namespace
+ */
+var jasmine = {};
+if (isCommonJS) exports.jasmine = jasmine;
+/**
+ * @private
+ */
+jasmine.unimplementedMethod_ = function() {
+ throw new Error("unimplemented method");
+};
+
+/**
+ * Use jasmine.undefined instead of undefined, since undefined is just
+ * a plain old variable and may be redefined by somebody else.
+ *
+ * @private
+ */
+jasmine.undefined = jasmine.___undefined___;
+
+/**
+ * Show diagnostic messages in the console if set to true
+ *
+ */
+jasmine.VERBOSE = false;
+
+/**
+ * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
+ *
+ */
+jasmine.DEFAULT_UPDATE_INTERVAL = 250;
+
+/**
+ * Maximum levels of nesting that will be included when an object is pretty-printed
+ */
+jasmine.MAX_PRETTY_PRINT_DEPTH = 40;
+
+/**
+ * Default timeout interval in milliseconds for waitsFor() blocks.
+ */
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
+
+/**
+ * By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite.
+ * Set to false to let the exception bubble up in the browser.
+ *
+ */
+jasmine.CATCH_EXCEPTIONS = true;
+
+jasmine.getGlobal = function() {
+ function getGlobal() {
+ return this;
+ }
+
+ return getGlobal();
+};
+
+/**
+ * Allows for bound functions to be compared. Internal use only.
+ *
+ * @ignore
+ * @private
+ * @param base {Object} bound 'this' for the function
+ * @param name {Function} function to find
+ */
+jasmine.bindOriginal_ = function(base, name) {
+ var original = base[name];
+ if (original.apply) {
+ return function() {
+ return original.apply(base, arguments);
+ };
+ } else {
+ // IE support
+ return jasmine.getGlobal()[name];
+ }
+};
+
+jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
+jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
+jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
+jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
+
+jasmine.MessageResult = function(values) {
+ this.type = 'log';
+ this.values = values;
+ this.trace = new Error(); // todo: test better
+};
+
+jasmine.MessageResult.prototype.toString = function() {
+ var text = "";
+ for (var i = 0; i < this.values.length; i++) {
+ if (i > 0) text += " ";
+ if (jasmine.isString_(this.values[i])) {
+ text += this.values[i];
+ } else {
+ text += jasmine.pp(this.values[i]);
+ }
+ }
+ return text;
+};
+
+jasmine.ExpectationResult = function(params) {
+ this.type = 'expect';
+ this.matcherName = params.matcherName;
+ this.passed_ = params.passed;
+ this.expected = params.expected;
+ this.actual = params.actual;
+ this.message = this.passed_ ? 'Passed.' : params.message;
+
+ var trace = (params.trace || new Error(this.message));
+ this.trace = this.passed_ ? '' : trace;
+};
+
+jasmine.ExpectationResult.prototype.toString = function () {
+ return this.message;
+};
+
+jasmine.ExpectationResult.prototype.passed = function () {
+ return this.passed_;
+};
+
+/**
+ * Getter for the Jasmine environment. Ensures one gets created
+ */
+jasmine.getEnv = function() {
+ var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
+ return env;
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isArray_ = function(value) {
+ return jasmine.isA_("Array", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isString_ = function(value) {
+ return jasmine.isA_("String", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isNumber_ = function(value) {
+ return jasmine.isA_("Number", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param {String} typeName
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isA_ = function(typeName, value) {
+ return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
+};
+
+/**
+ * Pretty printer for expecations. Takes any object and turns it into a human-readable string.
+ *
+ * @param value {Object} an object to be outputted
+ * @returns {String}
+ */
+jasmine.pp = function(value) {
+ var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
+ stringPrettyPrinter.format(value);
+ return stringPrettyPrinter.string;
+};
+
+/**
+ * Returns true if the object is a DOM Node.
+ *
+ * @param {Object} obj object to check
+ * @returns {Boolean}
+ */
+jasmine.isDomNode = function(obj) {
+ return obj.nodeType > 0;
+};
+
+/**
+ * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
+ *
+ * @example
+ * // don't care about which function is passed in, as long as it's a function
+ * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
+ *
+ * @param {Class} clazz
+ * @returns matchable object of the type clazz
+ */
+jasmine.any = function(clazz) {
+ return new jasmine.Matchers.Any(clazz);
+};
+
+/**
+ * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
+ * attributes on the object.
+ *
+ * @example
+ * // don't care about any other attributes than foo.
+ * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
+ *
+ * @param sample {Object} sample
+ * @returns matchable object for the sample
+ */
+jasmine.objectContaining = function (sample) {
+ return new jasmine.Matchers.ObjectContaining(sample);
+};
+
+/**
+ * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
+ *
+ * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
+ * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
+ *
+ * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
+ *
+ * Spies are torn down at the end of every spec.
+ *
+ * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
+ *
+ * @example
+ * // a stub
+ * var myStub = jasmine.createSpy('myStub'); // can be used anywhere
+ *
+ * // spy example
+ * var foo = {
+ * not: function(bool) { return !bool; }
+ * }
+ *
+ * // actual foo.not will not be called, execution stops
+ * spyOn(foo, 'not');
+
+ // foo.not spied upon, execution will continue to implementation
+ * spyOn(foo, 'not').andCallThrough();
+ *
+ * // fake example
+ * var foo = {
+ * not: function(bool) { return !bool; }
+ * }
+ *
+ * // foo.not(val) will return val
+ * spyOn(foo, 'not').andCallFake(function(value) {return value;});
+ *
+ * // mock example
+ * foo.not(7 == 7);
+ * expect(foo.not).toHaveBeenCalled();
+ * expect(foo.not).toHaveBeenCalledWith(true);
+ *
+ * @constructor
+ * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
+ * @param {String} name
+ */
+jasmine.Spy = function(name) {
+ /**
+ * The name of the spy, if provided.
+ */
+ this.identity = name || 'unknown';
+ /**
+ * Is this Object a spy?
+ */
+ this.isSpy = true;
+ /**
+ * The actual function this spy stubs.
+ */
+ this.plan = function() {
+ };
+ /**
+ * Tracking of the most recent call to the spy.
+ * @example
+ * var mySpy = jasmine.createSpy('foo');
+ * mySpy(1, 2);
+ * mySpy.mostRecentCall.args = [1, 2];
+ */
+ this.mostRecentCall = {};
+
+ /**
+ * Holds arguments for each call to the spy, indexed by call count
+ * @example
+ * var mySpy = jasmine.createSpy('foo');
+ * mySpy(1, 2);
+ * mySpy(7, 8);
+ * mySpy.mostRecentCall.args = [7, 8];
+ * mySpy.argsForCall[0] = [1, 2];
+ * mySpy.argsForCall[1] = [7, 8];
+ */
+ this.argsForCall = [];
+ this.calls = [];
+};
+
+/**
+ * Tells a spy to call through to the actual implemenatation.
+ *
+ * @example
+ * var foo = {
+ * bar: function() { // do some stuff }
+ * }
+ *
+ * // defining a spy on an existing property: foo.bar
+ * spyOn(foo, 'bar').andCallThrough();
+ */
+jasmine.Spy.prototype.andCallThrough = function() {
+ this.plan = this.originalValue;
+ return this;
+};
+
+/**
+ * For setting the return value of a spy.
+ *
+ * @example
+ * // defining a spy from scratch: foo() returns 'baz'
+ * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
+ *
+ * // defining a spy on an existing property: foo.bar() returns 'baz'
+ * spyOn(foo, 'bar').andReturn('baz');
+ *
+ * @param {Object} value
+ */
+jasmine.Spy.prototype.andReturn = function(value) {
+ this.plan = function() {
+ return value;
+ };
+ return this;
+};
+
+/**
+ * For throwing an exception when a spy is called.
+ *
+ * @example
+ * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
+ * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
+ *
+ * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
+ * spyOn(foo, 'bar').andThrow('baz');
+ *
+ * @param {String} exceptionMsg
+ */
+jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
+ this.plan = function() {
+ throw exceptionMsg;
+ };
+ return this;
+};
+
+/**
+ * Calls an alternate implementation when a spy is called.
+ *
+ * @example
+ * var baz = function() {
+ * // do some stuff, return something
+ * }
+ * // defining a spy from scratch: foo() calls the function baz
+ * var foo = jasmine.createSpy('spy on foo').andCall(baz);
+ *
+ * // defining a spy on an existing property: foo.bar() calls an anonymnous function
+ * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
+ *
+ * @param {Function} fakeFunc
+ */
+jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
+ this.plan = fakeFunc;
+ return this;
+};
+
+/**
+ * Resets all of a spy's the tracking variables so that it can be used again.
+ *
+ * @example
+ * spyOn(foo, 'bar');
+ *
+ * foo.bar();
+ *
+ * expect(foo.bar.callCount).toEqual(1);
+ *
+ * foo.bar.reset();
+ *
+ * expect(foo.bar.callCount).toEqual(0);
+ */
+jasmine.Spy.prototype.reset = function() {
+ this.wasCalled = false;
+ this.callCount = 0;
+ this.argsForCall = [];
+ this.calls = [];
+ this.mostRecentCall = {};
+};
+
+jasmine.createSpy = function(name) {
+
+ var spyObj = function() {
+ spyObj.wasCalled = true;
+ spyObj.callCount++;
+ var args = jasmine.util.argsToArray(arguments);
+ spyObj.mostRecentCall.object = this;
+ spyObj.mostRecentCall.args = args;
+ spyObj.argsForCall.push(args);
+ spyObj.calls.push({object: this, args: args});
+ return spyObj.plan.apply(this, arguments);
+ };
+
+ var spy = new jasmine.Spy(name);
+
+ for (var prop in spy) {
+ spyObj[prop] = spy[prop];
+ }
+
+ spyObj.reset();
+
+ return spyObj;
+};
+
+/**
+ * Determines whether an object is a spy.
+ *
+ * @param {jasmine.Spy|Object} putativeSpy
+ * @returns {Boolean}
+ */
+jasmine.isSpy = function(putativeSpy) {
+ return putativeSpy && putativeSpy.isSpy;
+};
+
+/**
+ * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
+ * large in one call.
+ *
+ * @param {String} baseName name of spy class
+ * @param {Array} methodNames array of names of methods to make spies
+ */
+jasmine.createSpyObj = function(baseName, methodNames) {
+ if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
+ throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
+ }
+ var obj = {};
+ for (var i = 0; i < methodNames.length; i++) {
+ obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
+ }
+ return obj;
+};
+
+/**
+ * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
+ *
+ * Be careful not to leave calls to jasmine.log in production code.
+ */
+jasmine.log = function() {
+ var spec = jasmine.getEnv().currentSpec;
+ spec.log.apply(spec, arguments);
+};
+
+/**
+ * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
+ *
+ * @example
+ * // spy example
+ * var foo = {
+ * not: function(bool) { return !bool; }
+ * }
+ * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
+ *
+ * @see jasmine.createSpy
+ * @param obj
+ * @param methodName
+ * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods
+ */
+var spyOn = function(obj, methodName) {
+ return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
+};
+if (isCommonJS) exports.spyOn = spyOn;
+
+/**
+ * Creates a Jasmine spec that will be added to the current suite.
+ *
+ * // TODO: pending tests
+ *
+ * @example
+ * it('should be true', function() {
+ * expect(true).toEqual(true);
+ * });
+ *
+ * @param {String} desc description of this specification
+ * @param {Function} func defines the preconditions and expectations of the spec
+ */
+var it = function(desc, func) {
+ return jasmine.getEnv().it(desc, func);
+};
+if (isCommonJS) exports.it = it;
+
+/**
+ * Creates a disabled Jasmine spec.
+ *
+ * A convenience method that allows existing specs to be disabled temporarily during development.
+ *
+ * @param {String} desc description of this specification
+ * @param {Function} func defines the preconditions and expectations of the spec
+ */
+var xit = function(desc, func) {
+ return jasmine.getEnv().xit(desc, func);
+};
+if (isCommonJS) exports.xit = xit;
+
+/**
+ * Starts a chain for a Jasmine expectation.
+ *
+ * It is passed an Object that is the actual value and should chain to one of the many
+ * jasmine.Matchers functions.
+ *
+ * @param {Object} actual Actual value to test against and expected value
+ * @return {jasmine.Matchers}
+ */
+var expect = function(actual) {
+ return jasmine.getEnv().currentSpec.expect(actual);
+};
+if (isCommonJS) exports.expect = expect;
+
+/**
+ * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
+ *
+ * @param {Function} func Function that defines part of a jasmine spec.
+ */
+var runs = function(func) {
+ jasmine.getEnv().currentSpec.runs(func);
+};
+if (isCommonJS) exports.runs = runs;
+
+/**
+ * Waits a fixed time period before moving to the next block.
+ *
+ * @deprecated Use waitsFor() instead
+ * @param {Number} timeout milliseconds to wait
+ */
+var waits = function(timeout) {
+ jasmine.getEnv().currentSpec.waits(timeout);
+};
+if (isCommonJS) exports.waits = waits;
+
+/**
+ * Waits for the latchFunction to return true before proceeding to the next block.
+ *
+ * @param {Function} latchFunction
+ * @param {String} optional_timeoutMessage
+ * @param {Number} optional_timeout
+ */
+var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
+ jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
+};
+if (isCommonJS) exports.waitsFor = waitsFor;
+
+/**
+ * A function that is called before each spec in a suite.
+ *
+ * Used for spec setup, including validating assumptions.
+ *
+ * @param {Function} beforeEachFunction
+ */
+var beforeEach = function(beforeEachFunction) {
+ jasmine.getEnv().beforeEach(beforeEachFunction);
+};
+if (isCommonJS) exports.beforeEach = beforeEach;
+
+/**
+ * A function that is called after each spec in a suite.
+ *
+ * Used for restoring any state that is hijacked during spec execution.
+ *
+ * @param {Function} afterEachFunction
+ */
+var afterEach = function(afterEachFunction) {
+ jasmine.getEnv().afterEach(afterEachFunction);
+};
+if (isCommonJS) exports.afterEach = afterEach;
+
+/**
+ * Defines a suite of specifications.
+ *
+ * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
+ * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
+ * of setup in some tests.
+ *
+ * @example
+ * // TODO: a simple suite
+ *
+ * // TODO: a simple suite with a nested describe block
+ *
+ * @param {String} description A string, usually the class under test.
+ * @param {Function} specDefinitions function that defines several specs.
+ */
+var describe = function(description, specDefinitions) {
+ return jasmine.getEnv().describe(description, specDefinitions);
+};
+if (isCommonJS) exports.describe = describe;
+
+/**
+ * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
+ *
+ * @param {String} description A string, usually the class under test.
+ * @param {Function} specDefinitions function that defines several specs.
+ */
+var xdescribe = function(description, specDefinitions) {
+ return jasmine.getEnv().xdescribe(description, specDefinitions);
+};
+if (isCommonJS) exports.xdescribe = xdescribe;
+
+
+// Provide the XMLHttpRequest class for IE 5.x-6.x:
+jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
+ function tryIt(f) {
+ try {
+ return f();
+ } catch(e) {
+ }
+ return null;
+ }
+
+ var xhr = tryIt(function() {
+ return new ActiveXObject("Msxml2.XMLHTTP.6.0");
+ }) ||
+ tryIt(function() {
+ return new ActiveXObject("Msxml2.XMLHTTP.3.0");
+ }) ||
+ tryIt(function() {
+ return new ActiveXObject("Msxml2.XMLHTTP");
+ }) ||
+ tryIt(function() {
+ return new ActiveXObject("Microsoft.XMLHTTP");
+ });
+
+ if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
+
+ return xhr;
+} : XMLHttpRequest;
+/**
+ * @namespace
+ */
+jasmine.util = {};
+
+/**
+ * Declare that a child class inherit it's prototype from the parent class.
+ *
+ * @private
+ * @param {Function} childClass
+ * @param {Function} parentClass
+ */
+jasmine.util.inherit = function(childClass, parentClass) {
+ /**
+ * @private
+ */
+ var subclass = function() {
+ };
+ subclass.prototype = parentClass.prototype;
+ childClass.prototype = new subclass();
+};
+
+jasmine.util.formatException = function(e) {
+ var lineNumber;
+ if (e.line) {
+ lineNumber = e.line;
+ }
+ else if (e.lineNumber) {
+ lineNumber = e.lineNumber;
+ }
+
+ var file;
+
+ if (e.sourceURL) {
+ file = e.sourceURL;
+ }
+ else if (e.fileName) {
+ file = e.fileName;
+ }
+
+ var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
+
+ if (file && lineNumber) {
+ message += ' in ' + file + ' (line ' + lineNumber + ')';
+ }
+
+ return message;
+};
+
+jasmine.util.htmlEscape = function(str) {
+ if (!str) return str;
+ return str.replace(/&/g, '&')
+ .replace(//g, '>');
+};
+
+jasmine.util.argsToArray = function(args) {
+ var arrayOfArgs = [];
+ for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
+ return arrayOfArgs;
+};
+
+jasmine.util.extend = function(destination, source) {
+ for (var property in source) destination[property] = source[property];
+ return destination;
+};
+
+/**
+ * Environment for Jasmine
+ *
+ * @constructor
+ */
+jasmine.Env = function() {
+ this.currentSpec = null;
+ this.currentSuite = null;
+ this.currentRunner_ = new jasmine.Runner(this);
+
+ this.reporter = new jasmine.MultiReporter();
+
+ this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
+ this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
+ this.lastUpdate = 0;
+ this.specFilter = function() {
+ return true;
+ };
+
+ this.nextSpecId_ = 0;
+ this.nextSuiteId_ = 0;
+ this.equalityTesters_ = [];
+
+ // wrap matchers
+ this.matchersClass = function() {
+ jasmine.Matchers.apply(this, arguments);
+ };
+ jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
+
+ jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
+};
+
+
+jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
+jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
+jasmine.Env.prototype.setInterval = jasmine.setInterval;
+jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
+
+/**
+ * @returns an object containing jasmine version build info, if set.
+ */
+jasmine.Env.prototype.version = function () {
+ if (jasmine.version_) {
+ return jasmine.version_;
+ } else {
+ throw new Error('Version not set');
+ }
+};
+
+/**
+ * @returns string containing jasmine version build info, if set.
+ */
+jasmine.Env.prototype.versionString = function() {
+ if (!jasmine.version_) {
+ return "version unknown";
+ }
+
+ var version = this.version();
+ var versionString = version.major + "." + version.minor + "." + version.build;
+ if (version.release_candidate) {
+ versionString += ".rc" + version.release_candidate;
+ }
+ versionString += " revision " + version.revision;
+ return versionString;
+};
+
+/**
+ * @returns a sequential integer starting at 0
+ */
+jasmine.Env.prototype.nextSpecId = function () {
+ return this.nextSpecId_++;
+};
+
+/**
+ * @returns a sequential integer starting at 0
+ */
+jasmine.Env.prototype.nextSuiteId = function () {
+ return this.nextSuiteId_++;
+};
+
+/**
+ * Register a reporter to receive status updates from Jasmine.
+ * @param {jasmine.Reporter} reporter An object which will receive status updates.
+ */
+jasmine.Env.prototype.addReporter = function(reporter) {
+ this.reporter.addReporter(reporter);
+};
+
+jasmine.Env.prototype.execute = function() {
+ this.currentRunner_.execute();
+};
+
+jasmine.Env.prototype.describe = function(description, specDefinitions) {
+ var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
+
+ var parentSuite = this.currentSuite;
+ if (parentSuite) {
+ parentSuite.add(suite);
+ } else {
+ this.currentRunner_.add(suite);
+ }
+
+ this.currentSuite = suite;
+
+ var declarationError = null;
+ try {
+ specDefinitions.call(suite);
+ } catch(e) {
+ declarationError = e;
+ }
+
+ if (declarationError) {
+ this.it("encountered a declaration exception", function() {
+ throw declarationError;
+ });
+ }
+
+ this.currentSuite = parentSuite;
+
+ return suite;
+};
+
+jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
+ if (this.currentSuite) {
+ this.currentSuite.beforeEach(beforeEachFunction);
+ } else {
+ this.currentRunner_.beforeEach(beforeEachFunction);
+ }
+};
+
+jasmine.Env.prototype.currentRunner = function () {
+ return this.currentRunner_;
+};
+
+jasmine.Env.prototype.afterEach = function(afterEachFunction) {
+ if (this.currentSuite) {
+ this.currentSuite.afterEach(afterEachFunction);
+ } else {
+ this.currentRunner_.afterEach(afterEachFunction);
+ }
+
+};
+
+jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
+ return {
+ execute: function() {
+ }
+ };
+};
+
+jasmine.Env.prototype.it = function(description, func) {
+ var spec = new jasmine.Spec(this, this.currentSuite, description);
+ this.currentSuite.add(spec);
+ this.currentSpec = spec;
+
+ if (func) {
+ spec.runs(func);
+ }
+
+ return spec;
+};
+
+jasmine.Env.prototype.xit = function(desc, func) {
+ return {
+ id: this.nextSpecId(),
+ runs: function() {
+ }
+ };
+};
+
+jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) {
+ if (a.source != b.source)
+ mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/");
+
+ if (a.ignoreCase != b.ignoreCase)
+ mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier");
+
+ if (a.global != b.global)
+ mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier");
+
+ if (a.multiline != b.multiline)
+ mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier");
+
+ if (a.sticky != b.sticky)
+ mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier");
+
+ return (mismatchValues.length === 0);
+};
+
+jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
+ if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
+ return true;
+ }
+
+ a.__Jasmine_been_here_before__ = b;
+ b.__Jasmine_been_here_before__ = a;
+
+ var hasKey = function(obj, keyName) {
+ return obj !== null && obj[keyName] !== jasmine.undefined;
+ };
+
+ for (var property in b) {
+ if (!hasKey(a, property) && hasKey(b, property)) {
+ mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
+ }
+ }
+ for (property in a) {
+ if (!hasKey(b, property) && hasKey(a, property)) {
+ mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
+ }
+ }
+ for (property in b) {
+ if (property == '__Jasmine_been_here_before__') continue;
+ if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
+ mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
+ }
+ }
+
+ if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
+ mismatchValues.push("arrays were not the same length");
+ }
+
+ delete a.__Jasmine_been_here_before__;
+ delete b.__Jasmine_been_here_before__;
+ return (mismatchKeys.length === 0 && mismatchValues.length === 0);
+};
+
+jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
+ mismatchKeys = mismatchKeys || [];
+ mismatchValues = mismatchValues || [];
+
+ for (var i = 0; i < this.equalityTesters_.length; i++) {
+ var equalityTester = this.equalityTesters_[i];
+ var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
+ if (result !== jasmine.undefined) return result;
+ }
+
+ if (a === b) return true;
+
+ if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
+ return (a == jasmine.undefined && b == jasmine.undefined);
+ }
+
+ if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
+ return a === b;
+ }
+
+ if (a instanceof Date && b instanceof Date) {
+ return a.getTime() == b.getTime();
+ }
+
+ if (a.jasmineMatches) {
+ return a.jasmineMatches(b);
+ }
+
+ if (b.jasmineMatches) {
+ return b.jasmineMatches(a);
+ }
+
+ if (a instanceof jasmine.Matchers.ObjectContaining) {
+ return a.matches(b);
+ }
+
+ if (b instanceof jasmine.Matchers.ObjectContaining) {
+ return b.matches(a);
+ }
+
+ if (jasmine.isString_(a) && jasmine.isString_(b)) {
+ return (a == b);
+ }
+
+ if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
+ return (a == b);
+ }
+
+ if (a instanceof RegExp && b instanceof RegExp) {
+ return this.compareRegExps_(a, b, mismatchKeys, mismatchValues);
+ }
+
+ if (typeof a === "object" && typeof b === "object") {
+ return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
+ }
+
+ //Straight check
+ return (a === b);
+};
+
+jasmine.Env.prototype.contains_ = function(haystack, needle) {
+ if (jasmine.isArray_(haystack)) {
+ for (var i = 0; i < haystack.length; i++) {
+ if (this.equals_(haystack[i], needle)) return true;
+ }
+ return false;
+ }
+ return haystack.indexOf(needle) >= 0;
+};
+
+jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
+ this.equalityTesters_.push(equalityTester);
+};
+/** No-op base class for Jasmine reporters.
+ *
+ * @constructor
+ */
+jasmine.Reporter = function() {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSpecResults = function(spec) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.log = function(str) {
+};
+
+/**
+ * Blocks are functions with executable code that make up a spec.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {Function} func
+ * @param {jasmine.Spec} spec
+ */
+jasmine.Block = function(env, func, spec) {
+ this.env = env;
+ this.func = func;
+ this.spec = spec;
+};
+
+jasmine.Block.prototype.execute = function(onComplete) {
+ if (!jasmine.CATCH_EXCEPTIONS) {
+ this.func.apply(this.spec);
+ }
+ else {
+ try {
+ this.func.apply(this.spec);
+ } catch (e) {
+ this.spec.fail(e);
+ }
+ }
+ onComplete();
+};
+/** JavaScript API reporter.
+ *
+ * @constructor
+ */
+jasmine.JsApiReporter = function() {
+ this.started = false;
+ this.finished = false;
+ this.suites_ = [];
+ this.results_ = {};
+};
+
+jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
+ this.started = true;
+ var suites = runner.topLevelSuites();
+ for (var i = 0; i < suites.length; i++) {
+ var suite = suites[i];
+ this.suites_.push(this.summarize_(suite));
+ }
+};
+
+jasmine.JsApiReporter.prototype.suites = function() {
+ return this.suites_;
+};
+
+jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
+ var isSuite = suiteOrSpec instanceof jasmine.Suite;
+ var summary = {
+ id: suiteOrSpec.id,
+ name: suiteOrSpec.description,
+ type: isSuite ? 'suite' : 'spec',
+ children: []
+ };
+
+ if (isSuite) {
+ var children = suiteOrSpec.children();
+ for (var i = 0; i < children.length; i++) {
+ summary.children.push(this.summarize_(children[i]));
+ }
+ }
+ return summary;
+};
+
+jasmine.JsApiReporter.prototype.results = function() {
+ return this.results_;
+};
+
+jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
+ return this.results_[specId];
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
+ this.finished = true;
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
+ this.results_[spec.id] = {
+ messages: spec.results().getItems(),
+ result: spec.results().failedCount > 0 ? "failed" : "passed"
+ };
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.log = function(str) {
+};
+
+jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
+ var results = {};
+ for (var i = 0; i < specIds.length; i++) {
+ var specId = specIds[i];
+ results[specId] = this.summarizeResult_(this.results_[specId]);
+ }
+ return results;
+};
+
+jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
+ var summaryMessages = [];
+ var messagesLength = result.messages.length;
+ for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
+ var resultMessage = result.messages[messageIndex];
+ summaryMessages.push({
+ text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
+ passed: resultMessage.passed ? resultMessage.passed() : true,
+ type: resultMessage.type,
+ message: resultMessage.message,
+ trace: {
+ stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
+ }
+ });
+ }
+
+ return {
+ result : result.result,
+ messages : summaryMessages
+ };
+};
+
+/**
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param actual
+ * @param {jasmine.Spec} spec
+ */
+jasmine.Matchers = function(env, actual, spec, opt_isNot) {
+ this.env = env;
+ this.actual = actual;
+ this.spec = spec;
+ this.isNot = opt_isNot || false;
+ this.reportWasCalled_ = false;
+};
+
+// todo: @deprecated as of Jasmine 0.11, remove soon [xw]
+jasmine.Matchers.pp = function(str) {
+ throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
+};
+
+// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
+jasmine.Matchers.prototype.report = function(result, failing_message, details) {
+ throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
+};
+
+jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
+ for (var methodName in prototype) {
+ if (methodName == 'report') continue;
+ var orig = prototype[methodName];
+ matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
+ }
+};
+
+jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
+ return function() {
+ var matcherArgs = jasmine.util.argsToArray(arguments);
+ var result = matcherFunction.apply(this, arguments);
+
+ if (this.isNot) {
+ result = !result;
+ }
+
+ if (this.reportWasCalled_) return result;
+
+ var message;
+ if (!result) {
+ if (this.message) {
+ message = this.message.apply(this, arguments);
+ if (jasmine.isArray_(message)) {
+ message = message[this.isNot ? 1 : 0];
+ }
+ } else {
+ var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
+ message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
+ if (matcherArgs.length > 0) {
+ for (var i = 0; i < matcherArgs.length; i++) {
+ if (i > 0) message += ",";
+ message += " " + jasmine.pp(matcherArgs[i]);
+ }
+ }
+ message += ".";
+ }
+ }
+ var expectationResult = new jasmine.ExpectationResult({
+ matcherName: matcherName,
+ passed: result,
+ expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
+ actual: this.actual,
+ message: message
+ });
+ this.spec.addMatcherResult(expectationResult);
+ return jasmine.undefined;
+ };
+};
+
+
+
+
+/**
+ * toBe: compares the actual to the expected using ===
+ * @param expected
+ */
+jasmine.Matchers.prototype.toBe = function(expected) {
+ return this.actual === expected;
+};
+
+/**
+ * toNotBe: compares the actual to the expected using !==
+ * @param expected
+ * @deprecated as of 1.0. Use not.toBe() instead.
+ */
+jasmine.Matchers.prototype.toNotBe = function(expected) {
+ return this.actual !== expected;
+};
+
+/**
+ * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
+ *
+ * @param expected
+ */
+jasmine.Matchers.prototype.toEqual = function(expected) {
+ return this.env.equals_(this.actual, expected);
+};
+
+/**
+ * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
+ * @param expected
+ * @deprecated as of 1.0. Use not.toEqual() instead.
+ */
+jasmine.Matchers.prototype.toNotEqual = function(expected) {
+ return !this.env.equals_(this.actual, expected);
+};
+
+/**
+ * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes
+ * a pattern or a String.
+ *
+ * @param expected
+ */
+jasmine.Matchers.prototype.toMatch = function(expected) {
+ return new RegExp(expected).test(this.actual);
+};
+
+/**
+ * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
+ * @param expected
+ * @deprecated as of 1.0. Use not.toMatch() instead.
+ */
+jasmine.Matchers.prototype.toNotMatch = function(expected) {
+ return !(new RegExp(expected).test(this.actual));
+};
+
+/**
+ * Matcher that compares the actual to jasmine.undefined.
+ */
+jasmine.Matchers.prototype.toBeDefined = function() {
+ return (this.actual !== jasmine.undefined);
+};
+
+/**
+ * Matcher that compares the actual to jasmine.undefined.
+ */
+jasmine.Matchers.prototype.toBeUndefined = function() {
+ return (this.actual === jasmine.undefined);
+};
+
+/**
+ * Matcher that compares the actual to null.
+ */
+jasmine.Matchers.prototype.toBeNull = function() {
+ return (this.actual === null);
+};
+
+/**
+ * Matcher that compares the actual to NaN.
+ */
+jasmine.Matchers.prototype.toBeNaN = function() {
+ this.message = function() {
+ return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ];
+ };
+
+ return (this.actual !== this.actual);
+};
+
+/**
+ * Matcher that boolean not-nots the actual.
+ */
+jasmine.Matchers.prototype.toBeTruthy = function() {
+ return !!this.actual;
+};
+
+
+/**
+ * Matcher that boolean nots the actual.
+ */
+jasmine.Matchers.prototype.toBeFalsy = function() {
+ return !this.actual;
+};
+
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was called.
+ */
+jasmine.Matchers.prototype.toHaveBeenCalled = function() {
+ if (arguments.length > 0) {
+ throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
+ }
+
+ if (!jasmine.isSpy(this.actual)) {
+ throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+ }
+
+ this.message = function() {
+ return [
+ "Expected spy " + this.actual.identity + " to have been called.",
+ "Expected spy " + this.actual.identity + " not to have been called."
+ ];
+ };
+
+ return this.actual.wasCalled;
+};
+
+/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
+jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was not called.
+ *
+ * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
+ */
+jasmine.Matchers.prototype.wasNotCalled = function() {
+ if (arguments.length > 0) {
+ throw new Error('wasNotCalled does not take arguments');
+ }
+
+ if (!jasmine.isSpy(this.actual)) {
+ throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+ }
+
+ this.message = function() {
+ return [
+ "Expected spy " + this.actual.identity + " to not have been called.",
+ "Expected spy " + this.actual.identity + " to have been called."
+ ];
+ };
+
+ return !this.actual.wasCalled;
+};
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
+ *
+ * @example
+ *
+ */
+jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
+ var expectedArgs = jasmine.util.argsToArray(arguments);
+ if (!jasmine.isSpy(this.actual)) {
+ throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+ }
+ this.message = function() {
+ var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was.";
+ var positiveMessage = "";
+ if (this.actual.callCount === 0) {
+ positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.";
+ } else {
+ positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but actual calls were " + jasmine.pp(this.actual.argsForCall).replace(/^\[ | \]$/g, '')
+ }
+ return [positiveMessage, invertedMessage];
+ };
+
+ return this.env.contains_(this.actual.argsForCall, expectedArgs);
+};
+
+/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
+jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
+
+/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
+jasmine.Matchers.prototype.wasNotCalledWith = function() {
+ var expectedArgs = jasmine.util.argsToArray(arguments);
+ if (!jasmine.isSpy(this.actual)) {
+ throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+ }
+
+ this.message = function() {
+ return [
+ "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
+ "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
+ ];
+ };
+
+ return !this.env.contains_(this.actual.argsForCall, expectedArgs);
+};
+
+/**
+ * Matcher that checks that the expected item is an element in the actual Array.
+ *
+ * @param {Object} expected
+ */
+jasmine.Matchers.prototype.toContain = function(expected) {
+ return this.env.contains_(this.actual, expected);
+};
+
+/**
+ * Matcher that checks that the expected item is NOT an element in the actual Array.
+ *
+ * @param {Object} expected
+ * @deprecated as of 1.0. Use not.toContain() instead.
+ */
+jasmine.Matchers.prototype.toNotContain = function(expected) {
+ return !this.env.contains_(this.actual, expected);
+};
+
+jasmine.Matchers.prototype.toBeLessThan = function(expected) {
+ return this.actual < expected;
+};
+
+jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
+ return this.actual > expected;
+};
+
+/**
+ * Matcher that checks that the expected item is equal to the actual item
+ * up to a given level of decimal precision (default 2).
+ *
+ * @param {Number} expected
+ * @param {Number} precision, as number of decimal places
+ */
+jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
+ if (!(precision === 0)) {
+ precision = precision || 2;
+ }
+ return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2);
+};
+
+/**
+ * Matcher that checks that the expected exception was thrown by the actual.
+ *
+ * @param {String} [expected]
+ */
+jasmine.Matchers.prototype.toThrow = function(expected) {
+ var result = false;
+ var exception;
+ if (typeof this.actual != 'function') {
+ throw new Error('Actual is not a function');
+ }
+ try {
+ this.actual();
+ } catch (e) {
+ exception = e;
+ }
+ if (exception) {
+ result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
+ }
+
+ var not = this.isNot ? "not " : "";
+
+ this.message = function() {
+ if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
+ return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
+ } else {
+ return "Expected function to throw an exception.";
+ }
+ };
+
+ return result;
+};
+
+jasmine.Matchers.Any = function(expectedClass) {
+ this.expectedClass = expectedClass;
+};
+
+jasmine.Matchers.Any.prototype.jasmineMatches = function(other) {
+ if (this.expectedClass == String) {
+ return typeof other == 'string' || other instanceof String;
+ }
+
+ if (this.expectedClass == Number) {
+ return typeof other == 'number' || other instanceof Number;
+ }
+
+ if (this.expectedClass == Function) {
+ return typeof other == 'function' || other instanceof Function;
+ }
+
+ if (this.expectedClass == Object) {
+ return typeof other == 'object';
+ }
+
+ return other instanceof this.expectedClass;
+};
+
+jasmine.Matchers.Any.prototype.jasmineToString = function() {
+ return '';
+};
+
+jasmine.Matchers.ObjectContaining = function (sample) {
+ this.sample = sample;
+};
+
+jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
+ mismatchKeys = mismatchKeys || [];
+ mismatchValues = mismatchValues || [];
+
+ var env = jasmine.getEnv();
+
+ var hasKey = function(obj, keyName) {
+ return obj != null && obj[keyName] !== jasmine.undefined;
+ };
+
+ for (var property in this.sample) {
+ if (!hasKey(other, property) && hasKey(this.sample, property)) {
+ mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
+ }
+ else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
+ mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
+ }
+ }
+
+ return (mismatchKeys.length === 0 && mismatchValues.length === 0);
+};
+
+jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {
+ return "";
+};
+// Mock setTimeout, clearTimeout
+// Contributed by Pivotal Computer Systems, www.pivotalsf.com
+
+jasmine.FakeTimer = function() {
+ this.reset();
+
+ var self = this;
+ self.setTimeout = function(funcToCall, millis) {
+ self.timeoutsMade++;
+ self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
+ return self.timeoutsMade;
+ };
+
+ self.setInterval = function(funcToCall, millis) {
+ self.timeoutsMade++;
+ self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
+ return self.timeoutsMade;
+ };
+
+ self.clearTimeout = function(timeoutKey) {
+ self.scheduledFunctions[timeoutKey] = jasmine.undefined;
+ };
+
+ self.clearInterval = function(timeoutKey) {
+ self.scheduledFunctions[timeoutKey] = jasmine.undefined;
+ };
+
+};
+
+jasmine.FakeTimer.prototype.reset = function() {
+ this.timeoutsMade = 0;
+ this.scheduledFunctions = {};
+ this.nowMillis = 0;
+};
+
+jasmine.FakeTimer.prototype.tick = function(millis) {
+ var oldMillis = this.nowMillis;
+ var newMillis = oldMillis + millis;
+ this.runFunctionsWithinRange(oldMillis, newMillis);
+ this.nowMillis = newMillis;
+};
+
+jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
+ var scheduledFunc;
+ var funcsToRun = [];
+ for (var timeoutKey in this.scheduledFunctions) {
+ scheduledFunc = this.scheduledFunctions[timeoutKey];
+ if (scheduledFunc != jasmine.undefined &&
+ scheduledFunc.runAtMillis >= oldMillis &&
+ scheduledFunc.runAtMillis <= nowMillis) {
+ funcsToRun.push(scheduledFunc);
+ this.scheduledFunctions[timeoutKey] = jasmine.undefined;
+ }
+ }
+
+ if (funcsToRun.length > 0) {
+ funcsToRun.sort(function(a, b) {
+ return a.runAtMillis - b.runAtMillis;
+ });
+ for (var i = 0; i < funcsToRun.length; ++i) {
+ try {
+ var funcToRun = funcsToRun[i];
+ this.nowMillis = funcToRun.runAtMillis;
+ funcToRun.funcToCall();
+ if (funcToRun.recurring) {
+ this.scheduleFunction(funcToRun.timeoutKey,
+ funcToRun.funcToCall,
+ funcToRun.millis,
+ true);
+ }
+ } catch(e) {
+ }
+ }
+ this.runFunctionsWithinRange(oldMillis, nowMillis);
+ }
+};
+
+jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
+ this.scheduledFunctions[timeoutKey] = {
+ runAtMillis: this.nowMillis + millis,
+ funcToCall: funcToCall,
+ recurring: recurring,
+ timeoutKey: timeoutKey,
+ millis: millis
+ };
+};
+
+/**
+ * @namespace
+ */
+jasmine.Clock = {
+ defaultFakeTimer: new jasmine.FakeTimer(),
+
+ reset: function() {
+ jasmine.Clock.assertInstalled();
+ jasmine.Clock.defaultFakeTimer.reset();
+ },
+
+ tick: function(millis) {
+ jasmine.Clock.assertInstalled();
+ jasmine.Clock.defaultFakeTimer.tick(millis);
+ },
+
+ runFunctionsWithinRange: function(oldMillis, nowMillis) {
+ jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
+ },
+
+ scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
+ jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
+ },
+
+ useMock: function() {
+ if (!jasmine.Clock.isInstalled()) {
+ var spec = jasmine.getEnv().currentSpec;
+ spec.after(jasmine.Clock.uninstallMock);
+
+ jasmine.Clock.installMock();
+ }
+ },
+
+ installMock: function() {
+ jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
+ },
+
+ uninstallMock: function() {
+ jasmine.Clock.assertInstalled();
+ jasmine.Clock.installed = jasmine.Clock.real;
+ },
+
+ real: {
+ setTimeout: jasmine.getGlobal().setTimeout,
+ clearTimeout: jasmine.getGlobal().clearTimeout,
+ setInterval: jasmine.getGlobal().setInterval,
+ clearInterval: jasmine.getGlobal().clearInterval
+ },
+
+ assertInstalled: function() {
+ if (!jasmine.Clock.isInstalled()) {
+ throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
+ }
+ },
+
+ isInstalled: function() {
+ return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
+ },
+
+ installed: null
+};
+jasmine.Clock.installed = jasmine.Clock.real;
+
+//else for IE support
+jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
+ if (jasmine.Clock.installed.setTimeout.apply) {
+ return jasmine.Clock.installed.setTimeout.apply(this, arguments);
+ } else {
+ return jasmine.Clock.installed.setTimeout(funcToCall, millis);
+ }
+};
+
+jasmine.getGlobal().setInterval = function(funcToCall, millis) {
+ if (jasmine.Clock.installed.setInterval.apply) {
+ return jasmine.Clock.installed.setInterval.apply(this, arguments);
+ } else {
+ return jasmine.Clock.installed.setInterval(funcToCall, millis);
+ }
+};
+
+jasmine.getGlobal().clearTimeout = function(timeoutKey) {
+ if (jasmine.Clock.installed.clearTimeout.apply) {
+ return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
+ } else {
+ return jasmine.Clock.installed.clearTimeout(timeoutKey);
+ }
+};
+
+jasmine.getGlobal().clearInterval = function(timeoutKey) {
+ if (jasmine.Clock.installed.clearTimeout.apply) {
+ return jasmine.Clock.installed.clearInterval.apply(this, arguments);
+ } else {
+ return jasmine.Clock.installed.clearInterval(timeoutKey);
+ }
+};
+
+/**
+ * @constructor
+ */
+jasmine.MultiReporter = function() {
+ this.subReporters_ = [];
+};
+jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
+
+jasmine.MultiReporter.prototype.addReporter = function(reporter) {
+ this.subReporters_.push(reporter);
+};
+
+(function() {
+ var functionNames = [
+ "reportRunnerStarting",
+ "reportRunnerResults",
+ "reportSuiteResults",
+ "reportSpecStarting",
+ "reportSpecResults",
+ "log"
+ ];
+ for (var i = 0; i < functionNames.length; i++) {
+ var functionName = functionNames[i];
+ jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
+ return function() {
+ for (var j = 0; j < this.subReporters_.length; j++) {
+ var subReporter = this.subReporters_[j];
+ if (subReporter[functionName]) {
+ subReporter[functionName].apply(subReporter, arguments);
+ }
+ }
+ };
+ })(functionName);
+ }
+})();
+/**
+ * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
+ *
+ * @constructor
+ */
+jasmine.NestedResults = function() {
+ /**
+ * The total count of results
+ */
+ this.totalCount = 0;
+ /**
+ * Number of passed results
+ */
+ this.passedCount = 0;
+ /**
+ * Number of failed results
+ */
+ this.failedCount = 0;
+ /**
+ * Was this suite/spec skipped?
+ */
+ this.skipped = false;
+ /**
+ * @ignore
+ */
+ this.items_ = [];
+};
+
+/**
+ * Roll up the result counts.
+ *
+ * @param result
+ */
+jasmine.NestedResults.prototype.rollupCounts = function(result) {
+ this.totalCount += result.totalCount;
+ this.passedCount += result.passedCount;
+ this.failedCount += result.failedCount;
+};
+
+/**
+ * Adds a log message.
+ * @param values Array of message parts which will be concatenated later.
+ */
+jasmine.NestedResults.prototype.log = function(values) {
+ this.items_.push(new jasmine.MessageResult(values));
+};
+
+/**
+ * Getter for the results: message & results.
+ */
+jasmine.NestedResults.prototype.getItems = function() {
+ return this.items_;
+};
+
+/**
+ * Adds a result, tracking counts (total, passed, & failed)
+ * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
+ */
+jasmine.NestedResults.prototype.addResult = function(result) {
+ if (result.type != 'log') {
+ if (result.items_) {
+ this.rollupCounts(result);
+ } else {
+ this.totalCount++;
+ if (result.passed()) {
+ this.passedCount++;
+ } else {
+ this.failedCount++;
+ }
+ }
+ }
+ this.items_.push(result);
+};
+
+/**
+ * @returns {Boolean} True if everything below passed
+ */
+jasmine.NestedResults.prototype.passed = function() {
+ return this.passedCount === this.totalCount;
+};
+/**
+ * Base class for pretty printing for expectation results.
+ */
+jasmine.PrettyPrinter = function() {
+ this.ppNestLevel_ = 0;
+};
+
+/**
+ * Formats a value in a nice, human-readable string.
+ *
+ * @param value
+ */
+jasmine.PrettyPrinter.prototype.format = function(value) {
+ this.ppNestLevel_++;
+ try {
+ if (value === jasmine.undefined) {
+ this.emitScalar('undefined');
+ } else if (value === null) {
+ this.emitScalar('null');
+ } else if (value === jasmine.getGlobal()) {
+ this.emitScalar('');
+ } else if (value.jasmineToString) {
+ this.emitScalar(value.jasmineToString());
+ } else if (typeof value === 'string') {
+ this.emitString(value);
+ } else if (jasmine.isSpy(value)) {
+ this.emitScalar("spy on " + value.identity);
+ } else if (value instanceof RegExp) {
+ this.emitScalar(value.toString());
+ } else if (typeof value === 'function') {
+ this.emitScalar('Function');
+ } else if (typeof value.nodeType === 'number') {
+ this.emitScalar('HTMLNode');
+ } else if (value instanceof Date) {
+ this.emitScalar('Date(' + value + ')');
+ } else if (value.__Jasmine_been_here_before__) {
+ this.emitScalar('');
+ } else if (jasmine.isArray_(value) || typeof value == 'object') {
+ value.__Jasmine_been_here_before__ = true;
+ if (jasmine.isArray_(value)) {
+ this.emitArray(value);
+ } else {
+ this.emitObject(value);
+ }
+ delete value.__Jasmine_been_here_before__;
+ } else {
+ this.emitScalar(value.toString());
+ }
+ } finally {
+ this.ppNestLevel_--;
+ }
+};
+
+jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
+ for (var property in obj) {
+ if (!obj.hasOwnProperty(property)) continue;
+ if (property == '__Jasmine_been_here_before__') continue;
+ fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined &&
+ obj.__lookupGetter__(property) !== null) : false);
+ }
+};
+
+jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
+
+jasmine.StringPrettyPrinter = function() {
+ jasmine.PrettyPrinter.call(this);
+
+ this.string = '';
+};
+jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
+
+jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
+ this.append(value);
+};
+
+jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
+ this.append("'" + value + "'");
+};
+
+jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
+ if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {
+ this.append("Array");
+ return;
+ }
+
+ this.append('[ ');
+ for (var i = 0; i < array.length; i++) {
+ if (i > 0) {
+ this.append(', ');
+ }
+ this.format(array[i]);
+ }
+ this.append(' ]');
+};
+
+jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
+ if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {
+ this.append("Object");
+ return;
+ }
+
+ var self = this;
+ this.append('{ ');
+ var first = true;
+
+ this.iterateObject(obj, function(property, isGetter) {
+ if (first) {
+ first = false;
+ } else {
+ self.append(', ');
+ }
+
+ self.append(property);
+ self.append(' : ');
+ if (isGetter) {
+ self.append('');
+ } else {
+ self.format(obj[property]);
+ }
+ });
+
+ this.append(' }');
+};
+
+jasmine.StringPrettyPrinter.prototype.append = function(value) {
+ this.string += value;
+};
+jasmine.Queue = function(env) {
+ this.env = env;
+
+ // parallel to blocks. each true value in this array means the block will
+ // get executed even if we abort
+ this.ensured = [];
+ this.blocks = [];
+ this.running = false;
+ this.index = 0;
+ this.offset = 0;
+ this.abort = false;
+};
+
+jasmine.Queue.prototype.addBefore = function(block, ensure) {
+ if (ensure === jasmine.undefined) {
+ ensure = false;
+ }
+
+ this.blocks.unshift(block);
+ this.ensured.unshift(ensure);
+};
+
+jasmine.Queue.prototype.add = function(block, ensure) {
+ if (ensure === jasmine.undefined) {
+ ensure = false;
+ }
+
+ this.blocks.push(block);
+ this.ensured.push(ensure);
+};
+
+jasmine.Queue.prototype.insertNext = function(block, ensure) {
+ if (ensure === jasmine.undefined) {
+ ensure = false;
+ }
+
+ this.ensured.splice((this.index + this.offset + 1), 0, ensure);
+ this.blocks.splice((this.index + this.offset + 1), 0, block);
+ this.offset++;
+};
+
+jasmine.Queue.prototype.start = function(onComplete) {
+ this.running = true;
+ this.onComplete = onComplete;
+ this.next_();
+};
+
+jasmine.Queue.prototype.isRunning = function() {
+ return this.running;
+};
+
+jasmine.Queue.LOOP_DONT_RECURSE = true;
+
+jasmine.Queue.prototype.next_ = function() {
+ var self = this;
+ var goAgain = true;
+
+ while (goAgain) {
+ goAgain = false;
+
+ if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) {
+ var calledSynchronously = true;
+ var completedSynchronously = false;
+
+ var onComplete = function () {
+ if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
+ completedSynchronously = true;
+ return;
+ }
+
+ if (self.blocks[self.index].abort) {
+ self.abort = true;
+ }
+
+ self.offset = 0;
+ self.index++;
+
+ var now = new Date().getTime();
+ if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
+ self.env.lastUpdate = now;
+ self.env.setTimeout(function() {
+ self.next_();
+ }, 0);
+ } else {
+ if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
+ goAgain = true;
+ } else {
+ self.next_();
+ }
+ }
+ };
+ self.blocks[self.index].execute(onComplete);
+
+ calledSynchronously = false;
+ if (completedSynchronously) {
+ onComplete();
+ }
+
+ } else {
+ self.running = false;
+ if (self.onComplete) {
+ self.onComplete();
+ }
+ }
+ }
+};
+
+jasmine.Queue.prototype.results = function() {
+ var results = new jasmine.NestedResults();
+ for (var i = 0; i < this.blocks.length; i++) {
+ if (this.blocks[i].results) {
+ results.addResult(this.blocks[i].results());
+ }
+ }
+ return results;
+};
+
+
+/**
+ * Runner
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ */
+jasmine.Runner = function(env) {
+ var self = this;
+ self.env = env;
+ self.queue = new jasmine.Queue(env);
+ self.before_ = [];
+ self.after_ = [];
+ self.suites_ = [];
+};
+
+jasmine.Runner.prototype.execute = function() {
+ var self = this;
+ if (self.env.reporter.reportRunnerStarting) {
+ self.env.reporter.reportRunnerStarting(this);
+ }
+ self.queue.start(function () {
+ self.finishCallback();
+ });
+};
+
+jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
+ beforeEachFunction.typeName = 'beforeEach';
+ this.before_.splice(0,0,beforeEachFunction);
+};
+
+jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
+ afterEachFunction.typeName = 'afterEach';
+ this.after_.splice(0,0,afterEachFunction);
+};
+
+
+jasmine.Runner.prototype.finishCallback = function() {
+ this.env.reporter.reportRunnerResults(this);
+};
+
+jasmine.Runner.prototype.addSuite = function(suite) {
+ this.suites_.push(suite);
+};
+
+jasmine.Runner.prototype.add = function(block) {
+ if (block instanceof jasmine.Suite) {
+ this.addSuite(block);
+ }
+ this.queue.add(block);
+};
+
+jasmine.Runner.prototype.specs = function () {
+ var suites = this.suites();
+ var specs = [];
+ for (var i = 0; i < suites.length; i++) {
+ specs = specs.concat(suites[i].specs());
+ }
+ return specs;
+};
+
+jasmine.Runner.prototype.suites = function() {
+ return this.suites_;
+};
+
+jasmine.Runner.prototype.topLevelSuites = function() {
+ var topLevelSuites = [];
+ for (var i = 0; i < this.suites_.length; i++) {
+ if (!this.suites_[i].parentSuite) {
+ topLevelSuites.push(this.suites_[i]);
+ }
+ }
+ return topLevelSuites;
+};
+
+jasmine.Runner.prototype.results = function() {
+ return this.queue.results();
+};
+/**
+ * Internal representation of a Jasmine specification, or test.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {jasmine.Suite} suite
+ * @param {String} description
+ */
+jasmine.Spec = function(env, suite, description) {
+ if (!env) {
+ throw new Error('jasmine.Env() required');
+ }
+ if (!suite) {
+ throw new Error('jasmine.Suite() required');
+ }
+ var spec = this;
+ spec.id = env.nextSpecId ? env.nextSpecId() : null;
+ spec.env = env;
+ spec.suite = suite;
+ spec.description = description;
+ spec.queue = new jasmine.Queue(env);
+
+ spec.afterCallbacks = [];
+ spec.spies_ = [];
+
+ spec.results_ = new jasmine.NestedResults();
+ spec.results_.description = description;
+ spec.matchersClass = null;
+};
+
+jasmine.Spec.prototype.getFullName = function() {
+ return this.suite.getFullName() + ' ' + this.description + '.';
+};
+
+
+jasmine.Spec.prototype.results = function() {
+ return this.results_;
+};
+
+/**
+ * All parameters are pretty-printed and concatenated together, then written to the spec's output.
+ *
+ * Be careful not to leave calls to jasmine.log in production code.
+ */
+jasmine.Spec.prototype.log = function() {
+ return this.results_.log(arguments);
+};
+
+jasmine.Spec.prototype.runs = function (func) {
+ var block = new jasmine.Block(this.env, func, this);
+ this.addToQueue(block);
+ return this;
+};
+
+jasmine.Spec.prototype.addToQueue = function (block) {
+ if (this.queue.isRunning()) {
+ this.queue.insertNext(block);
+ } else {
+ this.queue.add(block);
+ }
+};
+
+/**
+ * @param {jasmine.ExpectationResult} result
+ */
+jasmine.Spec.prototype.addMatcherResult = function(result) {
+ this.results_.addResult(result);
+};
+
+jasmine.Spec.prototype.expect = function(actual) {
+ var positive = new (this.getMatchersClass_())(this.env, actual, this);
+ positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
+ return positive;
+};
+
+/**
+ * Waits a fixed time period before moving to the next block.
+ *
+ * @deprecated Use waitsFor() instead
+ * @param {Number} timeout milliseconds to wait
+ */
+jasmine.Spec.prototype.waits = function(timeout) {
+ var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
+ this.addToQueue(waitsFunc);
+ return this;
+};
+
+/**
+ * Waits for the latchFunction to return true before proceeding to the next block.
+ *
+ * @param {Function} latchFunction
+ * @param {String} optional_timeoutMessage
+ * @param {Number} optional_timeout
+ */
+jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
+ var latchFunction_ = null;
+ var optional_timeoutMessage_ = null;
+ var optional_timeout_ = null;
+
+ for (var i = 0; i < arguments.length; i++) {
+ var arg = arguments[i];
+ switch (typeof arg) {
+ case 'function':
+ latchFunction_ = arg;
+ break;
+ case 'string':
+ optional_timeoutMessage_ = arg;
+ break;
+ case 'number':
+ optional_timeout_ = arg;
+ break;
+ }
+ }
+
+ var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
+ this.addToQueue(waitsForFunc);
+ return this;
+};
+
+jasmine.Spec.prototype.fail = function (e) {
+ var expectationResult = new jasmine.ExpectationResult({
+ passed: false,
+ message: e ? jasmine.util.formatException(e) : 'Exception',
+ trace: { stack: e.stack }
+ });
+ this.results_.addResult(expectationResult);
+};
+
+jasmine.Spec.prototype.getMatchersClass_ = function() {
+ return this.matchersClass || this.env.matchersClass;
+};
+
+jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
+ var parent = this.getMatchersClass_();
+ var newMatchersClass = function() {
+ parent.apply(this, arguments);
+ };
+ jasmine.util.inherit(newMatchersClass, parent);
+ jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
+ this.matchersClass = newMatchersClass;
+};
+
+jasmine.Spec.prototype.finishCallback = function() {
+ this.env.reporter.reportSpecResults(this);
+};
+
+jasmine.Spec.prototype.finish = function(onComplete) {
+ this.removeAllSpies();
+ this.finishCallback();
+ if (onComplete) {
+ onComplete();
+ }
+};
+
+jasmine.Spec.prototype.after = function(doAfter) {
+ if (this.queue.isRunning()) {
+ this.queue.add(new jasmine.Block(this.env, doAfter, this), true);
+ } else {
+ this.afterCallbacks.unshift(doAfter);
+ }
+};
+
+jasmine.Spec.prototype.execute = function(onComplete) {
+ var spec = this;
+ if (!spec.env.specFilter(spec)) {
+ spec.results_.skipped = true;
+ spec.finish(onComplete);
+ return;
+ }
+
+ this.env.reporter.reportSpecStarting(this);
+
+ spec.env.currentSpec = spec;
+
+ spec.addBeforesAndAftersToQueue();
+
+ spec.queue.start(function () {
+ spec.finish(onComplete);
+ });
+};
+
+jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
+ var runner = this.env.currentRunner();
+ var i;
+
+ for (var suite = this.suite; suite; suite = suite.parentSuite) {
+ for (i = 0; i < suite.before_.length; i++) {
+ this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
+ }
+ }
+ for (i = 0; i < runner.before_.length; i++) {
+ this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
+ }
+ for (i = 0; i < this.afterCallbacks.length; i++) {
+ this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true);
+ }
+ for (suite = this.suite; suite; suite = suite.parentSuite) {
+ for (i = 0; i < suite.after_.length; i++) {
+ this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true);
+ }
+ }
+ for (i = 0; i < runner.after_.length; i++) {
+ this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true);
+ }
+};
+
+jasmine.Spec.prototype.explodes = function() {
+ throw 'explodes function should not have been called';
+};
+
+jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
+ if (obj == jasmine.undefined) {
+ throw "spyOn could not find an object to spy upon for " + methodName + "()";
+ }
+
+ if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
+ throw methodName + '() method does not exist';
+ }
+
+ if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
+ throw new Error(methodName + ' has already been spied upon');
+ }
+
+ var spyObj = jasmine.createSpy(methodName);
+
+ this.spies_.push(spyObj);
+ spyObj.baseObj = obj;
+ spyObj.methodName = methodName;
+ spyObj.originalValue = obj[methodName];
+
+ obj[methodName] = spyObj;
+
+ return spyObj;
+};
+
+jasmine.Spec.prototype.removeAllSpies = function() {
+ for (var i = 0; i < this.spies_.length; i++) {
+ var spy = this.spies_[i];
+ spy.baseObj[spy.methodName] = spy.originalValue;
+ }
+ this.spies_ = [];
+};
+
+/**
+ * Internal representation of a Jasmine suite.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {String} description
+ * @param {Function} specDefinitions
+ * @param {jasmine.Suite} parentSuite
+ */
+jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
+ var self = this;
+ self.id = env.nextSuiteId ? env.nextSuiteId() : null;
+ self.description = description;
+ self.queue = new jasmine.Queue(env);
+ self.parentSuite = parentSuite;
+ self.env = env;
+ self.before_ = [];
+ self.after_ = [];
+ self.children_ = [];
+ self.suites_ = [];
+ self.specs_ = [];
+};
+
+jasmine.Suite.prototype.getFullName = function() {
+ var fullName = this.description;
+ for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
+ fullName = parentSuite.description + ' ' + fullName;
+ }
+ return fullName;
+};
+
+jasmine.Suite.prototype.finish = function(onComplete) {
+ this.env.reporter.reportSuiteResults(this);
+ this.finished = true;
+ if (typeof(onComplete) == 'function') {
+ onComplete();
+ }
+};
+
+jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
+ beforeEachFunction.typeName = 'beforeEach';
+ this.before_.unshift(beforeEachFunction);
+};
+
+jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
+ afterEachFunction.typeName = 'afterEach';
+ this.after_.unshift(afterEachFunction);
+};
+
+jasmine.Suite.prototype.results = function() {
+ return this.queue.results();
+};
+
+jasmine.Suite.prototype.add = function(suiteOrSpec) {
+ this.children_.push(suiteOrSpec);
+ if (suiteOrSpec instanceof jasmine.Suite) {
+ this.suites_.push(suiteOrSpec);
+ this.env.currentRunner().addSuite(suiteOrSpec);
+ } else {
+ this.specs_.push(suiteOrSpec);
+ }
+ this.queue.add(suiteOrSpec);
+};
+
+jasmine.Suite.prototype.specs = function() {
+ return this.specs_;
+};
+
+jasmine.Suite.prototype.suites = function() {
+ return this.suites_;
+};
+
+jasmine.Suite.prototype.children = function() {
+ return this.children_;
+};
+
+jasmine.Suite.prototype.execute = function(onComplete) {
+ var self = this;
+ this.queue.start(function () {
+ self.finish(onComplete);
+ });
+};
+jasmine.WaitsBlock = function(env, timeout, spec) {
+ this.timeout = timeout;
+ jasmine.Block.call(this, env, null, spec);
+};
+
+jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
+
+jasmine.WaitsBlock.prototype.execute = function (onComplete) {
+ if (jasmine.VERBOSE) {
+ this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
+ }
+ this.env.setTimeout(function () {
+ onComplete();
+ }, this.timeout);
+};
+/**
+ * A block which waits for some condition to become true, with timeout.
+ *
+ * @constructor
+ * @extends jasmine.Block
+ * @param {jasmine.Env} env The Jasmine environment.
+ * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
+ * @param {Function} latchFunction A function which returns true when the desired condition has been met.
+ * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
+ * @param {jasmine.Spec} spec The Jasmine spec.
+ */
+jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
+ this.timeout = timeout || env.defaultTimeoutInterval;
+ this.latchFunction = latchFunction;
+ this.message = message;
+ this.totalTimeSpentWaitingForLatch = 0;
+ jasmine.Block.call(this, env, null, spec);
+};
+jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
+
+jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
+
+jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
+ if (jasmine.VERBOSE) {
+ this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
+ }
+ var latchFunctionResult;
+ try {
+ latchFunctionResult = this.latchFunction.apply(this.spec);
+ } catch (e) {
+ this.spec.fail(e);
+ onComplete();
+ return;
+ }
+
+ if (latchFunctionResult) {
+ onComplete();
+ } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
+ var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
+ this.spec.fail({
+ name: 'timeout',
+ message: message
+ });
+
+ this.abort = true;
+ onComplete();
+ } else {
+ this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
+ var self = this;
+ this.env.setTimeout(function() {
+ self.execute(onComplete);
+ }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
+ }
+};
+
+jasmine.version_= {
+ "major": 1,
+ "minor": 3,
+ "build": 1,
+ "revision": 1354556913
+};
diff --git a/src/AttributeRouting.Tests/Integration/js/lib/jquery-1.9.1.min.js b/src/AttributeRouting.Tests/Integration/js/lib/jquery-1.9.1.min.js
new file mode 100644
index 0000000..39aae4e
--- /dev/null
+++ b/src/AttributeRouting.Tests/Integration/js/lib/jquery-1.9.1.min.js
@@ -0,0 +1,5 @@
+/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license
+//@ sourceMappingURL=jquery.min.map
+*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML=" a",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;
+return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,""],area:[1,""],param:[1,"",""],thead:[1,""],tr:[2,""],col:[2,""],td:[3,""],_default:b.support.htmlSerialize?[0,"",""]:[1,"X","
"]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>$2>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l)
+}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write(""),t.close(),n=ln(e,t),Pt.detach()),Gt[e]=n),n}function ln(e,t){var n=b(t.createElement(e)).appendTo(t.body),r=b.css(n[0],"display");return n.remove(),r}b.each(["height","width"],function(e,n){b.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(b.css(e,"display"))?b.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,i),i):0)}}}),b.support.opacity||(b.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=b.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===b.trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),b(function(){b.support.reliableMarginRight||(b.cssHooks.marginRight={get:function(e,n){return n?b.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!b.support.pixelPosition&&b.fn.position&&b.each(["top","left"],function(e,n){b.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?b(e).position()[n]+"px":r):t}}})}),b.expr&&b.expr.filters&&(b.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!b.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||b.css(e,"display"))},b.expr.filters.visible=function(e){return!b.expr.filters.hidden(e)}),b.each({margin:"",padding:"",border:"Width"},function(e,t){b.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(b.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=b.prop(this,"elements");return e?b.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!b(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Nt.test(e))}).map(function(e,t){var n=b(this).val();return null==n?null:b.isArray(n)?b.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),b.param=function(e,n){var r,i=[],o=function(e,t){t=b.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=b.ajaxSettings&&b.ajaxSettings.traditional),b.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(cn,"+")};function gn(e,t,n,r){var i;if(b.isArray(t))b.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==b.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}b.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){b.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),b.fn.hover=function(e,t){return this.mouseenter(e).mouseleave(t||e)};var mn,yn,vn=b.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Nn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Cn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=b.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=a.href}catch(Ln){yn=o.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(w)||[];if(b.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(u){var l;return o[u]=!0,b.each(e[u]||[],function(e,u){var c=u(n,r,i);return"string"!=typeof c||a||o[c]?a?!(l=c):t:(n.dataTypes.unshift(c),s(c),!1)}),l}return s(n.dataTypes[0])||!o["*"]&&s("*")}function Mn(e,n){var r,i,o=b.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&b.extend(!0,e,r),e}b.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(" ");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),b.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&b.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?b("").append(b.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},b.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){b.fn[t]=function(e){return this.on(t,e)}}),b.each(["get","post"],function(e,n){b[n]=function(e,r,i,o){return b.isFunction(r)&&(o=o||i,i=r,r=t),b.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Nn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":b.parseJSON,"text xml":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Mn(Mn(e,b.ajaxSettings),t):Mn(b.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,u,l,c,p=b.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?b(f):b.event,h=b.Deferred(),g=b.Callbacks("once memory"),m=p.statusCode||{},y={},v={},x=0,T="canceled",N={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)m[t]=[m[t],e[t]];else N.always(e[N.status]);return this},abort:function(e){var t=e||T;return l&&l.abort(t),k(0,t),this}};if(h.promise(N).complete=g.add,N.success=N.done,N.error=N.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=b.trim(p.dataType||"*").toLowerCase().match(w)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?80:443))==(mn[3]||("http:"===mn[1]?80:443)))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=b.param(p.data,p.traditional)),qn(An,p,n,N),2===x)return N;u=p.global,u&&0===b.active++&&b.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Cn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(b.lastModified[o]&&N.setRequestHeader("If-Modified-Since",b.lastModified[o]),b.etag[o]&&N.setRequestHeader("If-None-Match",b.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&N.setRequestHeader("Content-Type",p.contentType),N.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)N.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,N,p)===!1||2===x))return N.abort();T="abort";for(i in{success:1,error:1,complete:1})N[i](p[i]);if(l=qn(jn,p,n,N)){N.readyState=1,u&&d.trigger("ajaxSend",[N,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){N.abort("timeout")},p.timeout));try{x=1,l.send(y,k)}catch(C){if(!(2>x))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,C=n;2!==x&&(x=2,s&&clearTimeout(s),l=t,a=i||"",N.readyState=e>0?4:0,r&&(w=_n(p,N,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=N.getResponseHeader("Last-Modified"),T&&(b.lastModified[o]=T),T=N.getResponseHeader("etag"),T&&(b.etag[o]=T)),204===e?(c=!0,C="nocontent"):304===e?(c=!0,C="notmodified"):(c=Fn(p,w),C=c.state,y=c.data,v=c.error,c=!v)):(v=C,(e||!C)&&(C="error",0>e&&(e=0))),N.status=e,N.statusText=(n||C)+"",c?h.resolveWith(f,[y,C,N]):h.rejectWith(f,[N,C,v]),N.statusCode(m),m=t,u&&d.trigger(c?"ajaxSuccess":"ajaxError",[N,p,c?y:v]),g.fireWith(f,[N,C]),u&&(d.trigger("ajaxComplete",[N,p]),--b.active||b.event.trigger("ajaxStop")))}return N},getScript:function(e,n){return b.get(e,t,n,"script")},getJSON:function(e,t,n){return b.get(e,t,n,"json")}});function _n(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(s in c)s in r&&(n[c[s]]=r[s]);while("*"===l[0])l.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in u)if(u[s]&&u[s].test(o)){l.unshift(s);break}if(l[0]in r)a=l[0];else{for(s in r){if(!l[0]||e.converters[s+" "+l[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function Fn(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(i in e.converters)a[i.toLowerCase()]=e.converters[i];for(;r=u[++s];)if("*"!==r){if("*"!==l&&l!==r){if(i=a[l+" "+r]||a["* "+r],!i)for(n in a)if(o=n.split(" "),o[1]===r&&(i=a[l+" "+o[0]]||a["* "+o[0]])){i===!0?i=a[n]:a[n]!==!0&&(r=o[0],u.splice(s--,0,r));break}if(i!==!0)if(i&&e["throws"])t=i(t);else try{t=i(t)}catch(c){return{state:"parsererror",error:i?c:"No conversion from "+l+" to "+r}}}l=r}return{state:"success",data:t}}b.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),b.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=o.head||b("head")[0]||o.documentElement;return{send:function(t,i){n=o.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var On=[],Bn=/(=)\?(?=&|$)|\?\?/;b.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=On.pop()||b.expando+"_"+vn++;return this[e]=!0,e}}),b.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return u||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=b.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||b.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,On.push(o)),s&&b.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}b.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=b.ajaxSettings.xhr(),b.support.cors=!!Rn&&"withCredentials"in Rn,Rn=b.support.ajax=!!Rn,Rn&&b.ajaxTransport(function(n){if(!n.crossDomain||b.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=b.noop,$n&&delete Pn[a]),i)4!==u.readyState&&u.abort();else{p={},s=u.status,l=u.getAllResponseHeaders(),"string"==typeof u.responseText&&(p.text=u.responseText);try{c=u.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,l)},n.async?4===u.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},b(e).unload($n)),Pn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+x+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=Yn.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(b.cssNumber[e]?"":"px"),"px"!==r&&s){s=b.css(i.elem,e,!0)||n||1;do u=u||".5",s/=u,b.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=b.now()}function Zn(e,t){b.each(t,function(t,n){var r=(Qn[t]||[]).concat(Qn["*"]),i=0,o=r.length;for(;o>i;i++)if(r[i].call(e,t,n))return})}function er(e,t,n){var r,i,o=0,a=Gn.length,s=b.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;for(;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:b.extend({},t),opts:b.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=b.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(tr(c,l.opts.specialEasing);a>o;o++)if(r=Gn[o].call(l,e,c,l.opts))return r;return Zn(l,c),b.isFunction(l.opts.start)&&l.opts.start.call(e,l),b.fx.timer(b.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function tr(e,t){var n,r,i,o,a;for(i in e)if(r=b.camelCase(i),o=t[r],n=e[i],b.isArray(n)&&(o=n[1],n=e[i]=n[0]),i!==r&&(e[r]=n,delete e[i]),a=b.cssHooks[r],a&&"expand"in a){n=a.expand(n),delete e[r];for(i in n)i in e||(e[i]=n[i],t[i]=o)}else t[r]=o}b.Animation=b.extend(er,{tweener:function(e,t){b.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,u,l,c,p,f=this,d=e.style,h={},g=[],m=e.nodeType&&nn(e);n.queue||(c=b._queueHooks(e,"fx"),null==c.unqueued&&(c.unqueued=0,p=c.empty.fire,c.empty.fire=function(){c.unqueued||p()}),c.unqueued++,f.always(function(){f.always(function(){c.unqueued--,b.queue(e,"fx").length||c.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],"inline"===b.css(e,"display")&&"none"===b.css(e,"float")&&(b.support.inlineBlockNeedsLayout&&"inline"!==un(e.nodeName)?d.zoom=1:d.display="inline-block")),n.overflow&&(d.overflow="hidden",b.support.shrinkWrapBlocks||f.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(i in t)if(a=t[i],Vn.exec(a)){if(delete t[i],u=u||"toggle"===a,a===(m?"hide":"show"))continue;g.push(i)}if(o=g.length){s=b._data(e,"fxshow")||b._data(e,"fxshow",{}),"hidden"in s&&(m=s.hidden),u&&(s.hidden=!m),m?b(e).show():f.done(function(){b(e).hide()}),f.done(function(){var t;b._removeData(e,"fxshow");for(t in h)b.style(e,t,h[t])});for(i=0;o>i;i++)r=g[i],l=f.createTween(r,m?s[r]:0),h[r]=s[r]||b.style(e,r),r in s||(s[r]=l.start,m&&(l.end=l.start,l.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}b.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=b.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[b.cssProps[e.prop]]||b.cssHooks[e.prop])?b.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.each(["toggle","show","hide"],function(e,t){var n=b.fn[t];b.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),b.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=b.isEmptyObject(e),o=b.speed(t,n,r),a=function(){var t=er(this,b.extend({},e),o);a.finish=function(){t.stop(!0)},(i||b._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=b.timers,a=b._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&b.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=b._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=b.timers,a=r?r.length:0;for(n.finish=!0,b.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}b.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){b.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),b.speed=function(e,t,n){var r=e&&"object"==typeof e?b.extend({},e):{complete:n||!n&&t||b.isFunction(e)&&e,duration:e,easing:n&&t||t&&!b.isFunction(t)&&t};return r.duration=b.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in b.fx.speeds?b.fx.speeds[r.duration]:b.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){b.isFunction(r.old)&&r.old.call(this),r.queue&&b.dequeue(this,r.queue)},r},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},b.timers=[],b.fx=rr.prototype.init,b.fx.tick=function(){var e,n=b.timers,r=0;for(Xn=b.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||b.fx.stop(),Xn=t},b.fx.timer=function(e){e()&&b.timers.push(e)&&b.fx.start()},b.fx.interval=13,b.fx.start=function(){Un||(Un=setInterval(b.fx.tick,b.fx.interval))},b.fx.stop=function(){clearInterval(Un),Un=null},b.fx.speeds={slow:600,fast:200,_default:400},b.fx.step={},b.expr&&b.expr.filters&&(b.expr.filters.animated=function(e){return b.grep(b.timers,function(t){return e===t.elem}).length}),b.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){b.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,b.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},b.offset={setOffset:function(e,t,n){var r=b.css(e,"position");"static"===r&&(e.style.position="relative");var i=b(e),o=i.offset(),a=b.css(e,"top"),s=b.css(e,"left"),u=("absolute"===r||"fixed"===r)&&b.inArray("auto",[a,s])>-1,l={},c={},p,f;u?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),b.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(l.top=t.top-o.top+p),null!=t.left&&(l.left=t.left-o.left+f),"using"in t?t.using.call(e,l):i.css(l)}},b.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===b.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),b.nodeName(e[0],"html")||(n=e.offset()),n.top+=b.css(e[0],"borderTopWidth",!0),n.left+=b.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-b.css(r,"marginTop",!0),left:t.left-n.left-b.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||o.documentElement;while(e&&!b.nodeName(e,"html")&&"static"===b.css(e,"position"))e=e.offsetParent;return e||o.documentElement})}}),b.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);b.fn[e]=function(i){return b.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?b(a).scrollLeft():o,r?o:b(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return b.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}b.each({Height:"height",Width:"width"},function(e,n){b.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){b.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return b.access(this,function(n,r,i){var o;return b.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?b.css(n,r,s):b.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=b,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return b})})(window);
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Integration/js/specs/basic-usage.js b/src/AttributeRouting.Tests/Integration/js/specs/basic-usage.js
new file mode 100644
index 0000000..ed4b260
--- /dev/null
+++ b/src/AttributeRouting.Tests/Integration/js/specs/basic-usage.js
@@ -0,0 +1,16 @@
+///
+///
+
+describe('BasicUsageController', function () {
+ it('should respond to GET "/BasicUsage" with "BasicUsage.Index"', magic);
+ it('should respond to POST "/BasicUsage" with "BasicUsage.Create"', magic);
+ it('should respond to PUT "/BasicUsage/1" with "BasicUsage.Update(1)"', magic);
+ it('should respond to DELETE "/BasicUsage/1" with "BasicUsage.Delete(1)"', magic);
+});
+
+describe('HttpBasicUsageController', function () {
+ it('should respond to GET "/HttpBasicUsage" with "HttpBasicUsage.Index"', magic);
+ it('should respond to POST "/HttpBasicUsage" with "HttpBasicUsage.Create"', magic);
+ it('should respond to PUT "/HttpBasicUsage/1" with "HttpBasicUsage.Update(1)"', magic);
+ it('should respond to DELETE "/HttpBasicUsage/1" with "HttpBasicUsage.Delete(1)"', magic);
+});
diff --git a/src/AttributeRouting.Tests/Integration/js/specs/specs.js b/src/AttributeRouting.Tests/Integration/js/specs/specs.js
new file mode 100644
index 0000000..22bdc82
--- /dev/null
+++ b/src/AttributeRouting.Tests/Integration/js/specs/specs.js
@@ -0,0 +1,46 @@
+
+function magic() {
+ var description = this.description,
+ parser = new RegExp(/should respond to (\w+) "([\w-\/]+)" with "(.+?)"/i),
+ match = parser.exec(description),
+ method, url, expectedResponse;
+
+ // Validate the format of the spec's description.
+ if (!match || match.length !== 4) {
+ throw new Error('The spec description is not valid.\n' +
+ 'Expected: should respond to METHOD "/url" with "response"');
+ }
+
+ // Parse the relevant params from the description.
+ method = match[1];
+ url = match[2];
+ expectedResponse = match[3];
+
+ // Perform the assertion.
+ fetchResponse(method, url, function (response) {
+ expect(response).toEqual(expectedResponse);
+ });
+}
+
+function fetchResponse(method, url, callback) {
+ var response = undefined;
+
+ $.ajax({
+ type: method,
+ url: url,
+ success: function (res) {
+ response = res;
+ },
+ error: function (xhr, status) {
+ response = status + ': ' + xhr.responseText;
+ }
+ });
+
+ waitsFor(function () {
+ return response !== undefined;
+ }, 1000);
+
+ runs(function () {
+ callback(response);
+ });
+}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Integration/runner.html b/src/AttributeRouting.Tests/Integration/runner.html
new file mode 100644
index 0000000..ac666d7
--- /dev/null
+++ b/src/AttributeRouting.Tests/Integration/runner.html
@@ -0,0 +1,47 @@
+
+
+
+
AttributeRouting Integration Tests
+
+
+
+
+
+
Wanna see all the registered routes?
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AttributeRouting.Tests/Properties/AssemblyInfo.cs b/src/AttributeRouting.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..7e71053
--- /dev/null
+++ b/src/AttributeRouting.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("AttributeRouting.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AttributeRouting.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("b9f3a6cd-a204-4305-95a8-005982a72d13")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/AttributeRouting.Tests/Subjects/BasicUsageController.cs b/src/AttributeRouting.Tests/Subjects/BasicUsageController.cs
new file mode 100644
index 0000000..7d24e93
--- /dev/null
+++ b/src/AttributeRouting.Tests/Subjects/BasicUsageController.cs
@@ -0,0 +1,34 @@
+using System.Web.Mvc;
+using AttributeRouting.Helpers;
+using AttributeRouting.Web.Mvc;
+
+namespace AttributeRouting.Tests.Subjects
+{
+ [RoutePrefix("BasicUsage")]
+ public class BasicUsageController : Controller
+ {
+ [GET("")]
+ public string Index()
+ {
+ return "BasicUsage.Index";
+ }
+
+ [POST("")]
+ public string Create()
+ {
+ return "BasicUsage.Create";
+ }
+
+ [PUT("{id}")]
+ public string Update(string id)
+ {
+ return "BasicUsage.Update({0})".FormatWith(id);
+ }
+
+ [DELETE("{id}")]
+ public string Delete(string id)
+ {
+ return "BasicUsage.Delete({0})".FormatWith(id);
+ }
+ }
+}
diff --git a/src/AttributeRouting.Tests/Subjects/HttpBasicUsageController.cs b/src/AttributeRouting.Tests/Subjects/HttpBasicUsageController.cs
new file mode 100644
index 0000000..3a8ee30
--- /dev/null
+++ b/src/AttributeRouting.Tests/Subjects/HttpBasicUsageController.cs
@@ -0,0 +1,34 @@
+using System.Web.Http;
+using AttributeRouting.Helpers;
+using AttributeRouting.Web.Http;
+
+namespace AttributeRouting.Tests.Subjects
+{
+ [RoutePrefix("HttpBasicUsage")]
+ public class HttpBasicUsageController : ApiController
+ {
+ [GET(""), HttpGet]
+ public string Index()
+ {
+ return "HttpBasicUsage.Index";
+ }
+
+ [POST(""), HttpPost]
+ public string Create()
+ {
+ return "HttpBasicUsage.Create";
+ }
+
+ [PUT("{id}"), HttpPut]
+ public string Update(string id)
+ {
+ return "HttpBasicUsage.Update({0})".FormatWith(id);
+ }
+
+ [DELETE("{id}"), HttpDelete]
+ public string Delete(string id)
+ {
+ return "HttpBasicUsage.Delete({0})".FormatWith(id);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Web.Debug.config b/src/AttributeRouting.Tests/Web.Debug.config
new file mode 100644
index 0000000..a83c1d4
--- /dev/null
+++ b/src/AttributeRouting.Tests/Web.Debug.config
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Web.Release.config b/src/AttributeRouting.Tests/Web.Release.config
new file mode 100644
index 0000000..658b48e
--- /dev/null
+++ b/src/AttributeRouting.Tests/Web.Release.config
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Web.config b/src/AttributeRouting.Tests/Web.config
new file mode 100644
index 0000000..d47eb9f
--- /dev/null
+++ b/src/AttributeRouting.Tests/Web.config
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/packages.config b/src/AttributeRouting.Tests/packages.config
new file mode 100644
index 0000000..bb2b4dc
--- /dev/null
+++ b/src/AttributeRouting.Tests/packages.config
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AttributeRouting.sln b/src/AttributeRouting.sln
index 99fb999..96fba6a 100644
--- a/src/AttributeRouting.sln
+++ b/src/AttributeRouting.sln
@@ -33,6 +33,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Tests.Self
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Web.Http", "AttributeRouting.Web.Http\AttributeRouting.Web.Http.csproj", "{CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Tests", "AttributeRouting.Tests\AttributeRouting.Tests.csproj", "{2A6712B2-0912-457D-A66E-982640E9F411}"
+EndProject
Global
GlobalSection(SubversionScc) = preSolution
Svn-Managed = True
@@ -137,6 +139,16 @@ Global
{CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Release|x86.ActiveCfg = Release|Any CPU
+ {2A6712B2-0912-457D-A66E-982640E9F411}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2A6712B2-0912-457D-A66E-982640E9F411}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2A6712B2-0912-457D-A66E-982640E9F411}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {2A6712B2-0912-457D-A66E-982640E9F411}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {2A6712B2-0912-457D-A66E-982640E9F411}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2A6712B2-0912-457D-A66E-982640E9F411}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2A6712B2-0912-457D-A66E-982640E9F411}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2A6712B2-0912-457D-A66E-982640E9F411}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {2A6712B2-0912-457D-A66E-982640E9F411}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {2A6712B2-0912-457D-A66E-982640E9F411}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -145,5 +157,6 @@ Global
{E832A82B-2302-4113-83E3-261446E22C87} = {840281A8-8870-417F-9B24-ED0E866608A2}
{486087C6-E95B-4FE2-8263-7B6B2DF81888} = {840281A8-8870-417F-9B24-ED0E866608A2}
{0D2A13E6-A092-402E-B8FE-035F3A620B86} = {840281A8-8870-417F-9B24-ED0E866608A2}
+ {2A6712B2-0912-457D-A66E-982640E9F411} = {840281A8-8870-417F-9B24-ED0E866608A2}
EndGlobalSection
EndGlobal
From 7ee7956b7c68268841e2cca0f5a207b276d78a47 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Sun, 10 Mar 2013 13:21:20 -0600
Subject: [PATCH 78/97] Fixed bug in hanling of route defaults and querystring
params.
---
.../AttributeRouting.Tests.csproj | 8 +-
.../Integration/js/specs/basic-usage.js | 25 +++---
.../Integration/js/specs/route-defaults.js | 21 +++++
.../Integration/js/specs/specs.js | 89 ++++++++++---------
.../Integration/runner.html | 1 +
.../{ => Http}/HttpBasicUsageController.cs | 2 +-
.../Http/HttpRouteDefaultsController.cs | 34 +++++++
.../{ => Mvc}/BasicUsageController.cs | 2 +-
.../Subjects/Mvc/RouteDefaultsController.cs | 34 +++++++
.../Constraints/QueryStringRouteConstraint.cs | 14 +--
.../Constraints/QueryStringRouteConstraint.cs | 19 ++--
11 files changed, 177 insertions(+), 72 deletions(-)
create mode 100644 src/AttributeRouting.Tests/Integration/js/specs/route-defaults.js
rename src/AttributeRouting.Tests/Subjects/{ => Http}/HttpBasicUsageController.cs (90%)
create mode 100644 src/AttributeRouting.Tests/Subjects/Http/HttpRouteDefaultsController.cs
rename src/AttributeRouting.Tests/Subjects/{ => Mvc}/BasicUsageController.cs (89%)
create mode 100644 src/AttributeRouting.Tests/Subjects/Mvc/RouteDefaultsController.cs
diff --git a/src/AttributeRouting.Tests/AttributeRouting.Tests.csproj b/src/AttributeRouting.Tests/AttributeRouting.Tests.csproj
index f128f7d..35d89ca 100644
--- a/src/AttributeRouting.Tests/AttributeRouting.Tests.csproj
+++ b/src/AttributeRouting.Tests/AttributeRouting.Tests.csproj
@@ -114,8 +114,10 @@
Global.asax
-
-
+
+
+
+
@@ -124,6 +126,8 @@
+
+
diff --git a/src/AttributeRouting.Tests/Integration/js/specs/basic-usage.js b/src/AttributeRouting.Tests/Integration/js/specs/basic-usage.js
index ed4b260..6c9709b 100644
--- a/src/AttributeRouting.Tests/Integration/js/specs/basic-usage.js
+++ b/src/AttributeRouting.Tests/Integration/js/specs/basic-usage.js
@@ -1,16 +1,17 @@
///
///
-describe('BasicUsageController', function () {
- it('should respond to GET "/BasicUsage" with "BasicUsage.Index"', magic);
- it('should respond to POST "/BasicUsage" with "BasicUsage.Create"', magic);
- it('should respond to PUT "/BasicUsage/1" with "BasicUsage.Update(1)"', magic);
- it('should respond to DELETE "/BasicUsage/1" with "BasicUsage.Delete(1)"', magic);
-});
-
-describe('HttpBasicUsageController', function () {
- it('should respond to GET "/HttpBasicUsage" with "HttpBasicUsage.Index"', magic);
- it('should respond to POST "/HttpBasicUsage" with "HttpBasicUsage.Create"', magic);
- it('should respond to PUT "/HttpBasicUsage/1" with "HttpBasicUsage.Update(1)"', magic);
- it('should respond to DELETE "/HttpBasicUsage/1" with "HttpBasicUsage.Delete(1)"', magic);
+describe('Basic Usage', function() {
+ describe('BasicUsageController', function () {
+ it('should respond to GET "/BasicUsage" with "BasicUsage.Index"', specs.magic);
+ it('should respond to POST "/BasicUsage" with "BasicUsage.Create"', specs.magic);
+ it('should respond to PUT "/BasicUsage/1" with "BasicUsage.Update(1)"', specs.magic);
+ it('should respond to DELETE "/BasicUsage/1" with "BasicUsage.Delete(1)"', specs.magic);
+ });
+ describe('HttpBasicUsageController', function () {
+ it('should respond to GET "/HttpBasicUsage" with "HttpBasicUsage.Index"', specs.magic);
+ it('should respond to POST "/HttpBasicUsage" with "HttpBasicUsage.Create"', specs.magic);
+ it('should respond to PUT "/HttpBasicUsage/1" with "HttpBasicUsage.Update(1)"', specs.magic);
+ it('should respond to DELETE "/HttpBasicUsage/1" with "HttpBasicUsage.Delete(1)"', specs.magic);
+ });
});
diff --git a/src/AttributeRouting.Tests/Integration/js/specs/route-defaults.js b/src/AttributeRouting.Tests/Integration/js/specs/route-defaults.js
new file mode 100644
index 0000000..ab32962
--- /dev/null
+++ b/src/AttributeRouting.Tests/Integration/js/specs/route-defaults.js
@@ -0,0 +1,21 @@
+///
+///
+
+describe('Route Defaults', function() {
+ describe('RouteDefaultsController', function () {
+ it('should respond to GET "/RouteDefaults/Inline" with "RouteDefaults.Inline(param, query)"', specs.magic);
+ it('should respond to GET "/RouteDefaults/Optional" with "RouteDefaults.Optional(, )"', specs.magic);
+ it('should respond to GET "/RouteDefaults/Optional/param" with "RouteDefaults.Optional(param, )"', specs.magic);
+ it('should respond to GET "/RouteDefaults/Optional/?q=bleary" with "RouteDefaults.Optional(, bleary)"', specs.magic);
+ it('should respond to GET "/RouteDefaults/ControllerName" with "RouteDefaults.ControllerName"', specs.magic);
+ it('should respond to GET "/RouteDefaults/ActionName" with "RouteDefaults.ActionName"', specs.magic);
+ });
+ describe('HttpRouteDefaultsController', function () {
+ it('should respond to GET "/HttpRouteDefaults/Inline" with "HttpRouteDefaults.Inline(param, query)"', specs.magic);
+ it('should respond to GET "/HttpRouteDefaults/Optional" with "HttpRouteDefaults.Optional(, )"', specs.magic);
+ it('should respond to GET "/HttpRouteDefaults/Optional/param" with "HttpRouteDefaults.Optional(param, )"', specs.magic);
+ it('should respond to GET "/HttpRouteDefaults/Optional/?q=weary" with "HttpRouteDefaults.Optional(, weary)"', specs.magic);
+ it('should respond to GET "/HttpRouteDefaults/ControllerName" with "HttpRouteDefaults.ControllerName"', specs.magic);
+ it('should respond to GET "/HttpRouteDefaults/ActionName" with "HttpRouteDefaults.ActionName"', specs.magic);
+ });
+});
diff --git a/src/AttributeRouting.Tests/Integration/js/specs/specs.js b/src/AttributeRouting.Tests/Integration/js/specs/specs.js
index 22bdc82..adb809f 100644
--- a/src/AttributeRouting.Tests/Integration/js/specs/specs.js
+++ b/src/AttributeRouting.Tests/Integration/js/specs/specs.js
@@ -1,46 +1,49 @@
-
-function magic() {
- var description = this.description,
- parser = new RegExp(/should respond to (\w+) "([\w-\/]+)" with "(.+?)"/i),
- match = parser.exec(description),
- method, url, expectedResponse;
-
- // Validate the format of the spec's description.
- if (!match || match.length !== 4) {
- throw new Error('The spec description is not valid.\n' +
- 'Expected: should respond to METHOD "/url" with "response"');
- }
-
- // Parse the relevant params from the description.
- method = match[1];
- url = match[2];
- expectedResponse = match[3];
-
- // Perform the assertion.
- fetchResponse(method, url, function (response) {
- expect(response).toEqual(expectedResponse);
- });
-}
-
-function fetchResponse(method, url, callback) {
- var response = undefined;
-
- $.ajax({
- type: method,
- url: url,
- success: function (res) {
- response = res;
- },
- error: function (xhr, status) {
- response = status + ': ' + xhr.responseText;
+var specs = (function() {
+
+ var fetchResponse = function (method, url, callback) {
+ var response = undefined;
+
+ $.ajax({
+ type: method,
+ url: url,
+ success: function(res) {
+ response = res;
+ }
+ });
+
+ waitsFor(function() {
+ return response !== undefined;
+ }, 1000);
+
+ runs(function() {
+ callback(response);
+ });
+ };
+
+ var magic = function() {
+ var description = this.description,
+ parser = new RegExp(/should respond to (\w+) "([\w-\/\?=]+)" with "(.+?)"/i),
+ match = parser.exec(description),
+ method, url, expectedResponse;
+
+ // Validate the format of the spec's description.
+ if (!match || match.length !== 4) {
+ throw new Error('The spec description is not valid.\n' +
+ 'Expected: should respond to METHOD "/url" with "response"');
}
- });
- waitsFor(function () {
- return response !== undefined;
- }, 1000);
+ // Parse the relevant params from the description.
+ method = match[1];
+ url = match[2];
+ expectedResponse = match[3];
+
+ // Perform the assertion.
+ fetchResponse(method, url, function(response) {
+ expect(response).toEqual(expectedResponse);
+ });
+ };
- runs(function () {
- callback(response);
- });
-}
\ No newline at end of file
+ return {
+ magic: magic
+ };
+}());
diff --git a/src/AttributeRouting.Tests/Integration/runner.html b/src/AttributeRouting.Tests/Integration/runner.html
index ac666d7..73e7d5f 100644
--- a/src/AttributeRouting.Tests/Integration/runner.html
+++ b/src/AttributeRouting.Tests/Integration/runner.html
@@ -14,6 +14,7 @@
+
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/AttributeRouting.Tests/Integration/SpecRunnerController.cs b/src/AttributeRouting.Tests/Integration/SpecRunnerController.cs
deleted file mode 100644
index 48e21a3..0000000
--- a/src/AttributeRouting.Tests/Integration/SpecRunnerController.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.Web.Mvc;
-using AttributeRouting.Web.Mvc;
-
-namespace AttributeRouting.Tests.Integration
-{
- public class SpecRunnerController : Controller
- {
- [GET("", SitePrecedence = 1)]
- public ActionResult Index()
- {
- return View("~/Integration/SpecRunner.cshtml");
- }
- }
-}
diff --git a/src/AttributeRouting.Tests/Integration/css/jasmine.css b/src/AttributeRouting.Tests/Integration/css/jasmine.css
deleted file mode 100644
index 8c008dc..0000000
--- a/src/AttributeRouting.Tests/Integration/css/jasmine.css
+++ /dev/null
@@ -1,82 +0,0 @@
-body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
-
-#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
-#HTMLReporter a { text-decoration: none; }
-#HTMLReporter a:hover { text-decoration: underline; }
-#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
-#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
-#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
-#HTMLReporter .version { color: #aaaaaa; }
-#HTMLReporter .banner { margin-top: 14px; }
-#HTMLReporter .duration { color: #aaaaaa; float: right; }
-#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
-#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
-#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
-#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
-#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
-#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
-#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
-#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
-#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
-#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
-#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
-#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
-#HTMLReporter .runningAlert { background-color: #666666; }
-#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
-#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
-#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
-#HTMLReporter .passingAlert { background-color: #a6b779; }
-#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
-#HTMLReporter .failingAlert { background-color: #cf867e; }
-#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
-#HTMLReporter .results { margin-top: 14px; }
-#HTMLReporter #details { display: none; }
-#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
-#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
-#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
-#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
-#HTMLReporter.showDetails .summary { display: none; }
-#HTMLReporter.showDetails #details { display: block; }
-#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
-#HTMLReporter .summary { margin-top: 14px; }
-#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
-#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
-#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
-#HTMLReporter .description + .suite { margin-top: 0; }
-#HTMLReporter .suite { margin-top: 14px; }
-#HTMLReporter .suite a { color: #333333; }
-#HTMLReporter #details .specDetail { margin-bottom: 28px; }
-#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
-#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
-#HTMLReporter .resultMessage span.result { display: block; }
-#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
-
-#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
-#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
-#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
-#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
-#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
-#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
-#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
-#TrivialReporter .runner.running { background-color: yellow; }
-#TrivialReporter .options { text-align: right; font-size: .8em; }
-#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
-#TrivialReporter .suite .suite { margin: 5px; }
-#TrivialReporter .suite.passed { background-color: #dfd; }
-#TrivialReporter .suite.failed { background-color: #fdd; }
-#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
-#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
-#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
-#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
-#TrivialReporter .spec.skipped { background-color: #bbb; }
-#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
-#TrivialReporter .passed { background-color: #cfc; display: none; }
-#TrivialReporter .failed { background-color: #fbb; }
-#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
-#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
-#TrivialReporter .resultMessage .mismatch { color: black; }
-#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
-#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
-#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
-#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
-#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
diff --git a/src/AttributeRouting.Tests/Integration/css/runner.css b/src/AttributeRouting.Tests/Integration/css/runner.css
deleted file mode 100644
index 08d5025..0000000
--- a/src/AttributeRouting.Tests/Integration/css/runner.css
+++ /dev/null
@@ -1,9 +0,0 @@
-.view-all-routes {
- display: block;
- padding: 5px;
- font: 1em Helvetica, Arial, sans-serif;
- background-color: #333;
- color: white;
- text-decoration: none;
- text-align: center;
-}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Integration/js/lib/jasmine-html.js b/src/AttributeRouting.Tests/Integration/js/lib/jasmine-html.js
deleted file mode 100644
index 543d569..0000000
--- a/src/AttributeRouting.Tests/Integration/js/lib/jasmine-html.js
+++ /dev/null
@@ -1,681 +0,0 @@
-jasmine.HtmlReporterHelpers = {};
-
-jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
- var el = document.createElement(type);
-
- for (var i = 2; i < arguments.length; i++) {
- var child = arguments[i];
-
- if (typeof child === 'string') {
- el.appendChild(document.createTextNode(child));
- } else {
- if (child) {
- el.appendChild(child);
- }
- }
- }
-
- for (var attr in attrs) {
- if (attr == "className") {
- el[attr] = attrs[attr];
- } else {
- el.setAttribute(attr, attrs[attr]);
- }
- }
-
- return el;
-};
-
-jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
- var results = child.results();
- var status = results.passed() ? 'passed' : 'failed';
- if (results.skipped) {
- status = 'skipped';
- }
-
- return status;
-};
-
-jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
- var parentDiv = this.dom.summary;
- var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
- var parent = child[parentSuite];
-
- if (parent) {
- if (typeof this.views.suites[parent.id] == 'undefined') {
- this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
- }
- parentDiv = this.views.suites[parent.id].element;
- }
-
- parentDiv.appendChild(childElement);
-};
-
-
-jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
- for(var fn in jasmine.HtmlReporterHelpers) {
- ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
- }
-};
-
-jasmine.HtmlReporter = function(_doc) {
- var self = this;
- var doc = _doc || window.document;
-
- var reporterView;
-
- var dom = {};
-
- // Jasmine Reporter Public Interface
- self.logRunningSpecs = false;
-
- self.reportRunnerStarting = function(runner) {
- var specs = runner.specs() || [];
-
- if (specs.length == 0) {
- return;
- }
-
- createReporterDom(runner.env.versionString());
- doc.body.appendChild(dom.reporter);
- setExceptionHandling();
-
- reporterView = new jasmine.HtmlReporter.ReporterView(dom);
- reporterView.addSpecs(specs, self.specFilter);
- };
-
- self.reportRunnerResults = function(runner) {
- reporterView && reporterView.complete();
- };
-
- self.reportSuiteResults = function(suite) {
- reporterView.suiteComplete(suite);
- };
-
- self.reportSpecStarting = function(spec) {
- if (self.logRunningSpecs) {
- self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
- }
- };
-
- self.reportSpecResults = function(spec) {
- reporterView.specComplete(spec);
- };
-
- self.log = function() {
- var console = jasmine.getGlobal().console;
- if (console && console.log) {
- if (console.log.apply) {
- console.log.apply(console, arguments);
- } else {
- console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
- }
- }
- };
-
- self.specFilter = function(spec) {
- if (!focusedSpecName()) {
- return true;
- }
-
- return spec.getFullName().indexOf(focusedSpecName()) === 0;
- };
-
- return self;
-
- function focusedSpecName() {
- var specName;
-
- (function memoizeFocusedSpec() {
- if (specName) {
- return;
- }
-
- var paramMap = [];
- var params = jasmine.HtmlReporter.parameters(doc);
-
- for (var i = 0; i < params.length; i++) {
- var p = params[i].split('=');
- paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
- }
-
- specName = paramMap.spec;
- })();
-
- return specName;
- }
-
- function createReporterDom(version) {
- dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
- dom.banner = self.createDom('div', { className: 'banner' },
- self.createDom('span', { className: 'title' }, "Jasmine "),
- self.createDom('span', { className: 'version' }, version)),
-
- dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
- dom.alert = self.createDom('div', {className: 'alert'},
- self.createDom('span', { className: 'exceptions' },
- self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'),
- self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))),
- dom.results = self.createDom('div', {className: 'results'},
- dom.summary = self.createDom('div', { className: 'summary' }),
- dom.details = self.createDom('div', { id: 'details' }))
- );
- }
-
- function noTryCatch() {
- return window.location.search.match(/catch=false/);
- }
-
- function searchWithCatch() {
- var params = jasmine.HtmlReporter.parameters(window.document);
- var removed = false;
- var i = 0;
-
- while (!removed && i < params.length) {
- if (params[i].match(/catch=/)) {
- params.splice(i, 1);
- removed = true;
- }
- i++;
- }
- if (jasmine.CATCH_EXCEPTIONS) {
- params.push("catch=false");
- }
-
- return params.join("&");
- }
-
- function setExceptionHandling() {
- var chxCatch = document.getElementById('no_try_catch');
-
- if (noTryCatch()) {
- chxCatch.setAttribute('checked', true);
- jasmine.CATCH_EXCEPTIONS = false;
- }
- chxCatch.onclick = function() {
- window.location.search = searchWithCatch();
- };
- }
-};
-jasmine.HtmlReporter.parameters = function(doc) {
- var paramStr = doc.location.search.substring(1);
- var params = [];
-
- if (paramStr.length > 0) {
- params = paramStr.split('&');
- }
- return params;
-}
-jasmine.HtmlReporter.sectionLink = function(sectionName) {
- var link = '?';
- var params = [];
-
- if (sectionName) {
- params.push('spec=' + encodeURIComponent(sectionName));
- }
- if (!jasmine.CATCH_EXCEPTIONS) {
- params.push("catch=false");
- }
- if (params.length > 0) {
- link += params.join("&");
- }
-
- return link;
-};
-jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
-jasmine.HtmlReporter.ReporterView = function(dom) {
- this.startedAt = new Date();
- this.runningSpecCount = 0;
- this.completeSpecCount = 0;
- this.passedCount = 0;
- this.failedCount = 0;
- this.skippedCount = 0;
-
- this.createResultsMenu = function() {
- this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
- this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
- ' | ',
- this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
-
- this.summaryMenuItem.onclick = function() {
- dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
- };
-
- this.detailsMenuItem.onclick = function() {
- showDetails();
- };
- };
-
- this.addSpecs = function(specs, specFilter) {
- this.totalSpecCount = specs.length;
-
- this.views = {
- specs: {},
- suites: {}
- };
-
- for (var i = 0; i < specs.length; i++) {
- var spec = specs[i];
- this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
- if (specFilter(spec)) {
- this.runningSpecCount++;
- }
- }
- };
-
- this.specComplete = function(spec) {
- this.completeSpecCount++;
-
- if (isUndefined(this.views.specs[spec.id])) {
- this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
- }
-
- var specView = this.views.specs[spec.id];
-
- switch (specView.status()) {
- case 'passed':
- this.passedCount++;
- break;
-
- case 'failed':
- this.failedCount++;
- break;
-
- case 'skipped':
- this.skippedCount++;
- break;
- }
-
- specView.refresh();
- this.refresh();
- };
-
- this.suiteComplete = function(suite) {
- var suiteView = this.views.suites[suite.id];
- if (isUndefined(suiteView)) {
- return;
- }
- suiteView.refresh();
- };
-
- this.refresh = function() {
-
- if (isUndefined(this.resultsMenu)) {
- this.createResultsMenu();
- }
-
- // currently running UI
- if (isUndefined(this.runningAlert)) {
- this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" });
- dom.alert.appendChild(this.runningAlert);
- }
- this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
-
- // skipped specs UI
- if (isUndefined(this.skippedAlert)) {
- this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" });
- }
-
- this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
-
- if (this.skippedCount === 1 && isDefined(dom.alert)) {
- dom.alert.appendChild(this.skippedAlert);
- }
-
- // passing specs UI
- if (isUndefined(this.passedAlert)) {
- this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" });
- }
- this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
-
- // failing specs UI
- if (isUndefined(this.failedAlert)) {
- this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
- }
- this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
-
- if (this.failedCount === 1 && isDefined(dom.alert)) {
- dom.alert.appendChild(this.failedAlert);
- dom.alert.appendChild(this.resultsMenu);
- }
-
- // summary info
- this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
- this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
- };
-
- this.complete = function() {
- dom.alert.removeChild(this.runningAlert);
-
- this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
-
- if (this.failedCount === 0) {
- dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
- } else {
- showDetails();
- }
-
- dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
- };
-
- return this;
-
- function showDetails() {
- if (dom.reporter.className.search(/showDetails/) === -1) {
- dom.reporter.className += " showDetails";
- }
- }
-
- function isUndefined(obj) {
- return typeof obj === 'undefined';
- }
-
- function isDefined(obj) {
- return !isUndefined(obj);
- }
-
- function specPluralizedFor(count) {
- var str = count + " spec";
- if (count > 1) {
- str += "s"
- }
- return str;
- }
-
-};
-
-jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
-
-
-jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
- this.spec = spec;
- this.dom = dom;
- this.views = views;
-
- this.symbol = this.createDom('li', { className: 'pending' });
- this.dom.symbolSummary.appendChild(this.symbol);
-
- this.summary = this.createDom('div', { className: 'specSummary' },
- this.createDom('a', {
- className: 'description',
- href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),
- title: this.spec.getFullName()
- }, this.spec.description)
- );
-
- this.detail = this.createDom('div', { className: 'specDetail' },
- this.createDom('a', {
- className: 'description',
- href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
- title: this.spec.getFullName()
- }, this.spec.getFullName())
- );
-};
-
-jasmine.HtmlReporter.SpecView.prototype.status = function() {
- return this.getSpecStatus(this.spec);
-};
-
-jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
- this.symbol.className = this.status();
-
- switch (this.status()) {
- case 'skipped':
- break;
-
- case 'passed':
- this.appendSummaryToSuiteDiv();
- break;
-
- case 'failed':
- this.appendSummaryToSuiteDiv();
- this.appendFailureDetail();
- break;
- }
-};
-
-jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
- this.summary.className += ' ' + this.status();
- this.appendToSummary(this.spec, this.summary);
-};
-
-jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
- this.detail.className += ' ' + this.status();
-
- var resultItems = this.spec.results().getItems();
- var messagesDiv = this.createDom('div', { className: 'messages' });
-
- for (var i = 0; i < resultItems.length; i++) {
- var result = resultItems[i];
-
- if (result.type == 'log') {
- messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
- } else if (result.type == 'expect' && result.passed && !result.passed()) {
- messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
-
- if (result.trace.stack) {
- messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
- }
- }
- }
-
- if (messagesDiv.childNodes.length > 0) {
- this.detail.appendChild(messagesDiv);
- this.dom.details.appendChild(this.detail);
- }
-};
-
-jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
- this.suite = suite;
- this.dom = dom;
- this.views = views;
-
- this.element = this.createDom('div', { className: 'suite' },
- this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)
- );
-
- this.appendToSummary(this.suite, this.element);
-};
-
-jasmine.HtmlReporter.SuiteView.prototype.status = function() {
- return this.getSpecStatus(this.suite);
-};
-
-jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
- this.element.className += " " + this.status();
-};
-
-jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
-
-/* @deprecated Use jasmine.HtmlReporter instead
- */
-jasmine.TrivialReporter = function(doc) {
- this.document = doc || document;
- this.suiteDivs = {};
- this.logRunningSpecs = false;
-};
-
-jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
- var el = document.createElement(type);
-
- for (var i = 2; i < arguments.length; i++) {
- var child = arguments[i];
-
- if (typeof child === 'string') {
- el.appendChild(document.createTextNode(child));
- } else {
- if (child) { el.appendChild(child); }
- }
- }
-
- for (var attr in attrs) {
- if (attr == "className") {
- el[attr] = attrs[attr];
- } else {
- el.setAttribute(attr, attrs[attr]);
- }
- }
-
- return el;
-};
-
-jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
- var showPassed, showSkipped;
-
- this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
- this.createDom('div', { className: 'banner' },
- this.createDom('div', { className: 'logo' },
- this.createDom('span', { className: 'title' }, "Jasmine"),
- this.createDom('span', { className: 'version' }, runner.env.versionString())),
- this.createDom('div', { className: 'options' },
- "Show ",
- showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
- this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
- showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
- this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
- )
- ),
-
- this.runnerDiv = this.createDom('div', { className: 'runner running' },
- this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
- this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
- this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
- );
-
- this.document.body.appendChild(this.outerDiv);
-
- var suites = runner.suites();
- for (var i = 0; i < suites.length; i++) {
- var suite = suites[i];
- var suiteDiv = this.createDom('div', { className: 'suite' },
- this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
- this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
- this.suiteDivs[suite.id] = suiteDiv;
- var parentDiv = this.outerDiv;
- if (suite.parentSuite) {
- parentDiv = this.suiteDivs[suite.parentSuite.id];
- }
- parentDiv.appendChild(suiteDiv);
- }
-
- this.startedAt = new Date();
-
- var self = this;
- showPassed.onclick = function(evt) {
- if (showPassed.checked) {
- self.outerDiv.className += ' show-passed';
- } else {
- self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
- }
- };
-
- showSkipped.onclick = function(evt) {
- if (showSkipped.checked) {
- self.outerDiv.className += ' show-skipped';
- } else {
- self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
- }
- };
-};
-
-jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
- var results = runner.results();
- var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
- this.runnerDiv.setAttribute("class", className);
- //do it twice for IE
- this.runnerDiv.setAttribute("className", className);
- var specs = runner.specs();
- var specCount = 0;
- for (var i = 0; i < specs.length; i++) {
- if (this.specFilter(specs[i])) {
- specCount++;
- }
- }
- var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
- message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
- this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
-
- this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
-};
-
-jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
- var results = suite.results();
- var status = results.passed() ? 'passed' : 'failed';
- if (results.totalCount === 0) { // todo: change this to check results.skipped
- status = 'skipped';
- }
- this.suiteDivs[suite.id].className += " " + status;
-};
-
-jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
- if (this.logRunningSpecs) {
- this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
- }
-};
-
-jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
- var results = spec.results();
- var status = results.passed() ? 'passed' : 'failed';
- if (results.skipped) {
- status = 'skipped';
- }
- var specDiv = this.createDom('div', { className: 'spec ' + status },
- this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
- this.createDom('a', {
- className: 'description',
- href: '?spec=' + encodeURIComponent(spec.getFullName()),
- title: spec.getFullName()
- }, spec.description));
-
-
- var resultItems = results.getItems();
- var messagesDiv = this.createDom('div', { className: 'messages' });
- for (var i = 0; i < resultItems.length; i++) {
- var result = resultItems[i];
-
- if (result.type == 'log') {
- messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
- } else if (result.type == 'expect' && result.passed && !result.passed()) {
- messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
-
- if (result.trace.stack) {
- messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
- }
- }
- }
-
- if (messagesDiv.childNodes.length > 0) {
- specDiv.appendChild(messagesDiv);
- }
-
- this.suiteDivs[spec.suite.id].appendChild(specDiv);
-};
-
-jasmine.TrivialReporter.prototype.log = function() {
- var console = jasmine.getGlobal().console;
- if (console && console.log) {
- if (console.log.apply) {
- console.log.apply(console, arguments);
- } else {
- console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
- }
- }
-};
-
-jasmine.TrivialReporter.prototype.getLocation = function() {
- return this.document.location;
-};
-
-jasmine.TrivialReporter.prototype.specFilter = function(spec) {
- var paramMap = {};
- var params = this.getLocation().search.substring(1).split('&');
- for (var i = 0; i < params.length; i++) {
- var p = params[i].split('=');
- paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
- }
-
- if (!paramMap.spec) {
- return true;
- }
- return spec.getFullName().indexOf(paramMap.spec) === 0;
-};
diff --git a/src/AttributeRouting.Tests/Integration/js/lib/jasmine.js b/src/AttributeRouting.Tests/Integration/js/lib/jasmine.js
deleted file mode 100644
index 6b3459b..0000000
--- a/src/AttributeRouting.Tests/Integration/js/lib/jasmine.js
+++ /dev/null
@@ -1,2600 +0,0 @@
-var isCommonJS = typeof window == "undefined" && typeof exports == "object";
-
-/**
- * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
- *
- * @namespace
- */
-var jasmine = {};
-if (isCommonJS) exports.jasmine = jasmine;
-/**
- * @private
- */
-jasmine.unimplementedMethod_ = function() {
- throw new Error("unimplemented method");
-};
-
-/**
- * Use jasmine.undefined instead of undefined, since undefined is just
- * a plain old variable and may be redefined by somebody else.
- *
- * @private
- */
-jasmine.undefined = jasmine.___undefined___;
-
-/**
- * Show diagnostic messages in the console if set to true
- *
- */
-jasmine.VERBOSE = false;
-
-/**
- * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
- *
- */
-jasmine.DEFAULT_UPDATE_INTERVAL = 250;
-
-/**
- * Maximum levels of nesting that will be included when an object is pretty-printed
- */
-jasmine.MAX_PRETTY_PRINT_DEPTH = 40;
-
-/**
- * Default timeout interval in milliseconds for waitsFor() blocks.
- */
-jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
-
-/**
- * By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite.
- * Set to false to let the exception bubble up in the browser.
- *
- */
-jasmine.CATCH_EXCEPTIONS = true;
-
-jasmine.getGlobal = function() {
- function getGlobal() {
- return this;
- }
-
- return getGlobal();
-};
-
-/**
- * Allows for bound functions to be compared. Internal use only.
- *
- * @ignore
- * @private
- * @param base {Object} bound 'this' for the function
- * @param name {Function} function to find
- */
-jasmine.bindOriginal_ = function(base, name) {
- var original = base[name];
- if (original.apply) {
- return function() {
- return original.apply(base, arguments);
- };
- } else {
- // IE support
- return jasmine.getGlobal()[name];
- }
-};
-
-jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
-jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
-jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
-jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
-
-jasmine.MessageResult = function(values) {
- this.type = 'log';
- this.values = values;
- this.trace = new Error(); // todo: test better
-};
-
-jasmine.MessageResult.prototype.toString = function() {
- var text = "";
- for (var i = 0; i < this.values.length; i++) {
- if (i > 0) text += " ";
- if (jasmine.isString_(this.values[i])) {
- text += this.values[i];
- } else {
- text += jasmine.pp(this.values[i]);
- }
- }
- return text;
-};
-
-jasmine.ExpectationResult = function(params) {
- this.type = 'expect';
- this.matcherName = params.matcherName;
- this.passed_ = params.passed;
- this.expected = params.expected;
- this.actual = params.actual;
- this.message = this.passed_ ? 'Passed.' : params.message;
-
- var trace = (params.trace || new Error(this.message));
- this.trace = this.passed_ ? '' : trace;
-};
-
-jasmine.ExpectationResult.prototype.toString = function () {
- return this.message;
-};
-
-jasmine.ExpectationResult.prototype.passed = function () {
- return this.passed_;
-};
-
-/**
- * Getter for the Jasmine environment. Ensures one gets created
- */
-jasmine.getEnv = function() {
- var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
- return env;
-};
-
-/**
- * @ignore
- * @private
- * @param value
- * @returns {Boolean}
- */
-jasmine.isArray_ = function(value) {
- return jasmine.isA_("Array", value);
-};
-
-/**
- * @ignore
- * @private
- * @param value
- * @returns {Boolean}
- */
-jasmine.isString_ = function(value) {
- return jasmine.isA_("String", value);
-};
-
-/**
- * @ignore
- * @private
- * @param value
- * @returns {Boolean}
- */
-jasmine.isNumber_ = function(value) {
- return jasmine.isA_("Number", value);
-};
-
-/**
- * @ignore
- * @private
- * @param {String} typeName
- * @param value
- * @returns {Boolean}
- */
-jasmine.isA_ = function(typeName, value) {
- return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
-};
-
-/**
- * Pretty printer for expecations. Takes any object and turns it into a human-readable string.
- *
- * @param value {Object} an object to be outputted
- * @returns {String}
- */
-jasmine.pp = function(value) {
- var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
- stringPrettyPrinter.format(value);
- return stringPrettyPrinter.string;
-};
-
-/**
- * Returns true if the object is a DOM Node.
- *
- * @param {Object} obj object to check
- * @returns {Boolean}
- */
-jasmine.isDomNode = function(obj) {
- return obj.nodeType > 0;
-};
-
-/**
- * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
- *
- * @example
- * // don't care about which function is passed in, as long as it's a function
- * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
- *
- * @param {Class} clazz
- * @returns matchable object of the type clazz
- */
-jasmine.any = function(clazz) {
- return new jasmine.Matchers.Any(clazz);
-};
-
-/**
- * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
- * attributes on the object.
- *
- * @example
- * // don't care about any other attributes than foo.
- * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
- *
- * @param sample {Object} sample
- * @returns matchable object for the sample
- */
-jasmine.objectContaining = function (sample) {
- return new jasmine.Matchers.ObjectContaining(sample);
-};
-
-/**
- * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
- *
- * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
- * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
- *
- * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
- *
- * Spies are torn down at the end of every spec.
- *
- * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
- *
- * @example
- * // a stub
- * var myStub = jasmine.createSpy('myStub'); // can be used anywhere
- *
- * // spy example
- * var foo = {
- * not: function(bool) { return !bool; }
- * }
- *
- * // actual foo.not will not be called, execution stops
- * spyOn(foo, 'not');
-
- // foo.not spied upon, execution will continue to implementation
- * spyOn(foo, 'not').andCallThrough();
- *
- * // fake example
- * var foo = {
- * not: function(bool) { return !bool; }
- * }
- *
- * // foo.not(val) will return val
- * spyOn(foo, 'not').andCallFake(function(value) {return value;});
- *
- * // mock example
- * foo.not(7 == 7);
- * expect(foo.not).toHaveBeenCalled();
- * expect(foo.not).toHaveBeenCalledWith(true);
- *
- * @constructor
- * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
- * @param {String} name
- */
-jasmine.Spy = function(name) {
- /**
- * The name of the spy, if provided.
- */
- this.identity = name || 'unknown';
- /**
- * Is this Object a spy?
- */
- this.isSpy = true;
- /**
- * The actual function this spy stubs.
- */
- this.plan = function() {
- };
- /**
- * Tracking of the most recent call to the spy.
- * @example
- * var mySpy = jasmine.createSpy('foo');
- * mySpy(1, 2);
- * mySpy.mostRecentCall.args = [1, 2];
- */
- this.mostRecentCall = {};
-
- /**
- * Holds arguments for each call to the spy, indexed by call count
- * @example
- * var mySpy = jasmine.createSpy('foo');
- * mySpy(1, 2);
- * mySpy(7, 8);
- * mySpy.mostRecentCall.args = [7, 8];
- * mySpy.argsForCall[0] = [1, 2];
- * mySpy.argsForCall[1] = [7, 8];
- */
- this.argsForCall = [];
- this.calls = [];
-};
-
-/**
- * Tells a spy to call through to the actual implemenatation.
- *
- * @example
- * var foo = {
- * bar: function() { // do some stuff }
- * }
- *
- * // defining a spy on an existing property: foo.bar
- * spyOn(foo, 'bar').andCallThrough();
- */
-jasmine.Spy.prototype.andCallThrough = function() {
- this.plan = this.originalValue;
- return this;
-};
-
-/**
- * For setting the return value of a spy.
- *
- * @example
- * // defining a spy from scratch: foo() returns 'baz'
- * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
- *
- * // defining a spy on an existing property: foo.bar() returns 'baz'
- * spyOn(foo, 'bar').andReturn('baz');
- *
- * @param {Object} value
- */
-jasmine.Spy.prototype.andReturn = function(value) {
- this.plan = function() {
- return value;
- };
- return this;
-};
-
-/**
- * For throwing an exception when a spy is called.
- *
- * @example
- * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
- * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
- *
- * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
- * spyOn(foo, 'bar').andThrow('baz');
- *
- * @param {String} exceptionMsg
- */
-jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
- this.plan = function() {
- throw exceptionMsg;
- };
- return this;
-};
-
-/**
- * Calls an alternate implementation when a spy is called.
- *
- * @example
- * var baz = function() {
- * // do some stuff, return something
- * }
- * // defining a spy from scratch: foo() calls the function baz
- * var foo = jasmine.createSpy('spy on foo').andCall(baz);
- *
- * // defining a spy on an existing property: foo.bar() calls an anonymnous function
- * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
- *
- * @param {Function} fakeFunc
- */
-jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
- this.plan = fakeFunc;
- return this;
-};
-
-/**
- * Resets all of a spy's the tracking variables so that it can be used again.
- *
- * @example
- * spyOn(foo, 'bar');
- *
- * foo.bar();
- *
- * expect(foo.bar.callCount).toEqual(1);
- *
- * foo.bar.reset();
- *
- * expect(foo.bar.callCount).toEqual(0);
- */
-jasmine.Spy.prototype.reset = function() {
- this.wasCalled = false;
- this.callCount = 0;
- this.argsForCall = [];
- this.calls = [];
- this.mostRecentCall = {};
-};
-
-jasmine.createSpy = function(name) {
-
- var spyObj = function() {
- spyObj.wasCalled = true;
- spyObj.callCount++;
- var args = jasmine.util.argsToArray(arguments);
- spyObj.mostRecentCall.object = this;
- spyObj.mostRecentCall.args = args;
- spyObj.argsForCall.push(args);
- spyObj.calls.push({object: this, args: args});
- return spyObj.plan.apply(this, arguments);
- };
-
- var spy = new jasmine.Spy(name);
-
- for (var prop in spy) {
- spyObj[prop] = spy[prop];
- }
-
- spyObj.reset();
-
- return spyObj;
-};
-
-/**
- * Determines whether an object is a spy.
- *
- * @param {jasmine.Spy|Object} putativeSpy
- * @returns {Boolean}
- */
-jasmine.isSpy = function(putativeSpy) {
- return putativeSpy && putativeSpy.isSpy;
-};
-
-/**
- * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
- * large in one call.
- *
- * @param {String} baseName name of spy class
- * @param {Array} methodNames array of names of methods to make spies
- */
-jasmine.createSpyObj = function(baseName, methodNames) {
- if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
- throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
- }
- var obj = {};
- for (var i = 0; i < methodNames.length; i++) {
- obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
- }
- return obj;
-};
-
-/**
- * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
- *
- * Be careful not to leave calls to jasmine.log in production code.
- */
-jasmine.log = function() {
- var spec = jasmine.getEnv().currentSpec;
- spec.log.apply(spec, arguments);
-};
-
-/**
- * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
- *
- * @example
- * // spy example
- * var foo = {
- * not: function(bool) { return !bool; }
- * }
- * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
- *
- * @see jasmine.createSpy
- * @param obj
- * @param methodName
- * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods
- */
-var spyOn = function(obj, methodName) {
- return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
-};
-if (isCommonJS) exports.spyOn = spyOn;
-
-/**
- * Creates a Jasmine spec that will be added to the current suite.
- *
- * // TODO: pending tests
- *
- * @example
- * it('should be true', function() {
- * expect(true).toEqual(true);
- * });
- *
- * @param {String} desc description of this specification
- * @param {Function} func defines the preconditions and expectations of the spec
- */
-var it = function(desc, func) {
- return jasmine.getEnv().it(desc, func);
-};
-if (isCommonJS) exports.it = it;
-
-/**
- * Creates a disabled Jasmine spec.
- *
- * A convenience method that allows existing specs to be disabled temporarily during development.
- *
- * @param {String} desc description of this specification
- * @param {Function} func defines the preconditions and expectations of the spec
- */
-var xit = function(desc, func) {
- return jasmine.getEnv().xit(desc, func);
-};
-if (isCommonJS) exports.xit = xit;
-
-/**
- * Starts a chain for a Jasmine expectation.
- *
- * It is passed an Object that is the actual value and should chain to one of the many
- * jasmine.Matchers functions.
- *
- * @param {Object} actual Actual value to test against and expected value
- * @return {jasmine.Matchers}
- */
-var expect = function(actual) {
- return jasmine.getEnv().currentSpec.expect(actual);
-};
-if (isCommonJS) exports.expect = expect;
-
-/**
- * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
- *
- * @param {Function} func Function that defines part of a jasmine spec.
- */
-var runs = function(func) {
- jasmine.getEnv().currentSpec.runs(func);
-};
-if (isCommonJS) exports.runs = runs;
-
-/**
- * Waits a fixed time period before moving to the next block.
- *
- * @deprecated Use waitsFor() instead
- * @param {Number} timeout milliseconds to wait
- */
-var waits = function(timeout) {
- jasmine.getEnv().currentSpec.waits(timeout);
-};
-if (isCommonJS) exports.waits = waits;
-
-/**
- * Waits for the latchFunction to return true before proceeding to the next block.
- *
- * @param {Function} latchFunction
- * @param {String} optional_timeoutMessage
- * @param {Number} optional_timeout
- */
-var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
- jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
-};
-if (isCommonJS) exports.waitsFor = waitsFor;
-
-/**
- * A function that is called before each spec in a suite.
- *
- * Used for spec setup, including validating assumptions.
- *
- * @param {Function} beforeEachFunction
- */
-var beforeEach = function(beforeEachFunction) {
- jasmine.getEnv().beforeEach(beforeEachFunction);
-};
-if (isCommonJS) exports.beforeEach = beforeEach;
-
-/**
- * A function that is called after each spec in a suite.
- *
- * Used for restoring any state that is hijacked during spec execution.
- *
- * @param {Function} afterEachFunction
- */
-var afterEach = function(afterEachFunction) {
- jasmine.getEnv().afterEach(afterEachFunction);
-};
-if (isCommonJS) exports.afterEach = afterEach;
-
-/**
- * Defines a suite of specifications.
- *
- * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
- * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
- * of setup in some tests.
- *
- * @example
- * // TODO: a simple suite
- *
- * // TODO: a simple suite with a nested describe block
- *
- * @param {String} description A string, usually the class under test.
- * @param {Function} specDefinitions function that defines several specs.
- */
-var describe = function(description, specDefinitions) {
- return jasmine.getEnv().describe(description, specDefinitions);
-};
-if (isCommonJS) exports.describe = describe;
-
-/**
- * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
- *
- * @param {String} description A string, usually the class under test.
- * @param {Function} specDefinitions function that defines several specs.
- */
-var xdescribe = function(description, specDefinitions) {
- return jasmine.getEnv().xdescribe(description, specDefinitions);
-};
-if (isCommonJS) exports.xdescribe = xdescribe;
-
-
-// Provide the XMLHttpRequest class for IE 5.x-6.x:
-jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
- function tryIt(f) {
- try {
- return f();
- } catch(e) {
- }
- return null;
- }
-
- var xhr = tryIt(function() {
- return new ActiveXObject("Msxml2.XMLHTTP.6.0");
- }) ||
- tryIt(function() {
- return new ActiveXObject("Msxml2.XMLHTTP.3.0");
- }) ||
- tryIt(function() {
- return new ActiveXObject("Msxml2.XMLHTTP");
- }) ||
- tryIt(function() {
- return new ActiveXObject("Microsoft.XMLHTTP");
- });
-
- if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
-
- return xhr;
-} : XMLHttpRequest;
-/**
- * @namespace
- */
-jasmine.util = {};
-
-/**
- * Declare that a child class inherit it's prototype from the parent class.
- *
- * @private
- * @param {Function} childClass
- * @param {Function} parentClass
- */
-jasmine.util.inherit = function(childClass, parentClass) {
- /**
- * @private
- */
- var subclass = function() {
- };
- subclass.prototype = parentClass.prototype;
- childClass.prototype = new subclass();
-};
-
-jasmine.util.formatException = function(e) {
- var lineNumber;
- if (e.line) {
- lineNumber = e.line;
- }
- else if (e.lineNumber) {
- lineNumber = e.lineNumber;
- }
-
- var file;
-
- if (e.sourceURL) {
- file = e.sourceURL;
- }
- else if (e.fileName) {
- file = e.fileName;
- }
-
- var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
-
- if (file && lineNumber) {
- message += ' in ' + file + ' (line ' + lineNumber + ')';
- }
-
- return message;
-};
-
-jasmine.util.htmlEscape = function(str) {
- if (!str) return str;
- return str.replace(/&/g, '&')
- .replace(//g, '>');
-};
-
-jasmine.util.argsToArray = function(args) {
- var arrayOfArgs = [];
- for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
- return arrayOfArgs;
-};
-
-jasmine.util.extend = function(destination, source) {
- for (var property in source) destination[property] = source[property];
- return destination;
-};
-
-/**
- * Environment for Jasmine
- *
- * @constructor
- */
-jasmine.Env = function() {
- this.currentSpec = null;
- this.currentSuite = null;
- this.currentRunner_ = new jasmine.Runner(this);
-
- this.reporter = new jasmine.MultiReporter();
-
- this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
- this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
- this.lastUpdate = 0;
- this.specFilter = function() {
- return true;
- };
-
- this.nextSpecId_ = 0;
- this.nextSuiteId_ = 0;
- this.equalityTesters_ = [];
-
- // wrap matchers
- this.matchersClass = function() {
- jasmine.Matchers.apply(this, arguments);
- };
- jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
-
- jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
-};
-
-
-jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
-jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
-jasmine.Env.prototype.setInterval = jasmine.setInterval;
-jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
-
-/**
- * @returns an object containing jasmine version build info, if set.
- */
-jasmine.Env.prototype.version = function () {
- if (jasmine.version_) {
- return jasmine.version_;
- } else {
- throw new Error('Version not set');
- }
-};
-
-/**
- * @returns string containing jasmine version build info, if set.
- */
-jasmine.Env.prototype.versionString = function() {
- if (!jasmine.version_) {
- return "version unknown";
- }
-
- var version = this.version();
- var versionString = version.major + "." + version.minor + "." + version.build;
- if (version.release_candidate) {
- versionString += ".rc" + version.release_candidate;
- }
- versionString += " revision " + version.revision;
- return versionString;
-};
-
-/**
- * @returns a sequential integer starting at 0
- */
-jasmine.Env.prototype.nextSpecId = function () {
- return this.nextSpecId_++;
-};
-
-/**
- * @returns a sequential integer starting at 0
- */
-jasmine.Env.prototype.nextSuiteId = function () {
- return this.nextSuiteId_++;
-};
-
-/**
- * Register a reporter to receive status updates from Jasmine.
- * @param {jasmine.Reporter} reporter An object which will receive status updates.
- */
-jasmine.Env.prototype.addReporter = function(reporter) {
- this.reporter.addReporter(reporter);
-};
-
-jasmine.Env.prototype.execute = function() {
- this.currentRunner_.execute();
-};
-
-jasmine.Env.prototype.describe = function(description, specDefinitions) {
- var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
-
- var parentSuite = this.currentSuite;
- if (parentSuite) {
- parentSuite.add(suite);
- } else {
- this.currentRunner_.add(suite);
- }
-
- this.currentSuite = suite;
-
- var declarationError = null;
- try {
- specDefinitions.call(suite);
- } catch(e) {
- declarationError = e;
- }
-
- if (declarationError) {
- this.it("encountered a declaration exception", function() {
- throw declarationError;
- });
- }
-
- this.currentSuite = parentSuite;
-
- return suite;
-};
-
-jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
- if (this.currentSuite) {
- this.currentSuite.beforeEach(beforeEachFunction);
- } else {
- this.currentRunner_.beforeEach(beforeEachFunction);
- }
-};
-
-jasmine.Env.prototype.currentRunner = function () {
- return this.currentRunner_;
-};
-
-jasmine.Env.prototype.afterEach = function(afterEachFunction) {
- if (this.currentSuite) {
- this.currentSuite.afterEach(afterEachFunction);
- } else {
- this.currentRunner_.afterEach(afterEachFunction);
- }
-
-};
-
-jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
- return {
- execute: function() {
- }
- };
-};
-
-jasmine.Env.prototype.it = function(description, func) {
- var spec = new jasmine.Spec(this, this.currentSuite, description);
- this.currentSuite.add(spec);
- this.currentSpec = spec;
-
- if (func) {
- spec.runs(func);
- }
-
- return spec;
-};
-
-jasmine.Env.prototype.xit = function(desc, func) {
- return {
- id: this.nextSpecId(),
- runs: function() {
- }
- };
-};
-
-jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) {
- if (a.source != b.source)
- mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/");
-
- if (a.ignoreCase != b.ignoreCase)
- mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier");
-
- if (a.global != b.global)
- mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier");
-
- if (a.multiline != b.multiline)
- mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier");
-
- if (a.sticky != b.sticky)
- mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier");
-
- return (mismatchValues.length === 0);
-};
-
-jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
- if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
- return true;
- }
-
- a.__Jasmine_been_here_before__ = b;
- b.__Jasmine_been_here_before__ = a;
-
- var hasKey = function(obj, keyName) {
- return obj !== null && obj[keyName] !== jasmine.undefined;
- };
-
- for (var property in b) {
- if (!hasKey(a, property) && hasKey(b, property)) {
- mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
- }
- }
- for (property in a) {
- if (!hasKey(b, property) && hasKey(a, property)) {
- mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
- }
- }
- for (property in b) {
- if (property == '__Jasmine_been_here_before__') continue;
- if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
- mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
- }
- }
-
- if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
- mismatchValues.push("arrays were not the same length");
- }
-
- delete a.__Jasmine_been_here_before__;
- delete b.__Jasmine_been_here_before__;
- return (mismatchKeys.length === 0 && mismatchValues.length === 0);
-};
-
-jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
- mismatchKeys = mismatchKeys || [];
- mismatchValues = mismatchValues || [];
-
- for (var i = 0; i < this.equalityTesters_.length; i++) {
- var equalityTester = this.equalityTesters_[i];
- var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
- if (result !== jasmine.undefined) return result;
- }
-
- if (a === b) return true;
-
- if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
- return (a == jasmine.undefined && b == jasmine.undefined);
- }
-
- if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
- return a === b;
- }
-
- if (a instanceof Date && b instanceof Date) {
- return a.getTime() == b.getTime();
- }
-
- if (a.jasmineMatches) {
- return a.jasmineMatches(b);
- }
-
- if (b.jasmineMatches) {
- return b.jasmineMatches(a);
- }
-
- if (a instanceof jasmine.Matchers.ObjectContaining) {
- return a.matches(b);
- }
-
- if (b instanceof jasmine.Matchers.ObjectContaining) {
- return b.matches(a);
- }
-
- if (jasmine.isString_(a) && jasmine.isString_(b)) {
- return (a == b);
- }
-
- if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
- return (a == b);
- }
-
- if (a instanceof RegExp && b instanceof RegExp) {
- return this.compareRegExps_(a, b, mismatchKeys, mismatchValues);
- }
-
- if (typeof a === "object" && typeof b === "object") {
- return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
- }
-
- //Straight check
- return (a === b);
-};
-
-jasmine.Env.prototype.contains_ = function(haystack, needle) {
- if (jasmine.isArray_(haystack)) {
- for (var i = 0; i < haystack.length; i++) {
- if (this.equals_(haystack[i], needle)) return true;
- }
- return false;
- }
- return haystack.indexOf(needle) >= 0;
-};
-
-jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
- this.equalityTesters_.push(equalityTester);
-};
-/** No-op base class for Jasmine reporters.
- *
- * @constructor
- */
-jasmine.Reporter = function() {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportSpecResults = function(spec) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.log = function(str) {
-};
-
-/**
- * Blocks are functions with executable code that make up a spec.
- *
- * @constructor
- * @param {jasmine.Env} env
- * @param {Function} func
- * @param {jasmine.Spec} spec
- */
-jasmine.Block = function(env, func, spec) {
- this.env = env;
- this.func = func;
- this.spec = spec;
-};
-
-jasmine.Block.prototype.execute = function(onComplete) {
- if (!jasmine.CATCH_EXCEPTIONS) {
- this.func.apply(this.spec);
- }
- else {
- try {
- this.func.apply(this.spec);
- } catch (e) {
- this.spec.fail(e);
- }
- }
- onComplete();
-};
-/** JavaScript API reporter.
- *
- * @constructor
- */
-jasmine.JsApiReporter = function() {
- this.started = false;
- this.finished = false;
- this.suites_ = [];
- this.results_ = {};
-};
-
-jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
- this.started = true;
- var suites = runner.topLevelSuites();
- for (var i = 0; i < suites.length; i++) {
- var suite = suites[i];
- this.suites_.push(this.summarize_(suite));
- }
-};
-
-jasmine.JsApiReporter.prototype.suites = function() {
- return this.suites_;
-};
-
-jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
- var isSuite = suiteOrSpec instanceof jasmine.Suite;
- var summary = {
- id: suiteOrSpec.id,
- name: suiteOrSpec.description,
- type: isSuite ? 'suite' : 'spec',
- children: []
- };
-
- if (isSuite) {
- var children = suiteOrSpec.children();
- for (var i = 0; i < children.length; i++) {
- summary.children.push(this.summarize_(children[i]));
- }
- }
- return summary;
-};
-
-jasmine.JsApiReporter.prototype.results = function() {
- return this.results_;
-};
-
-jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
- return this.results_[specId];
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
- this.finished = true;
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
- this.results_[spec.id] = {
- messages: spec.results().getItems(),
- result: spec.results().failedCount > 0 ? "failed" : "passed"
- };
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.log = function(str) {
-};
-
-jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
- var results = {};
- for (var i = 0; i < specIds.length; i++) {
- var specId = specIds[i];
- results[specId] = this.summarizeResult_(this.results_[specId]);
- }
- return results;
-};
-
-jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
- var summaryMessages = [];
- var messagesLength = result.messages.length;
- for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
- var resultMessage = result.messages[messageIndex];
- summaryMessages.push({
- text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
- passed: resultMessage.passed ? resultMessage.passed() : true,
- type: resultMessage.type,
- message: resultMessage.message,
- trace: {
- stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
- }
- });
- }
-
- return {
- result : result.result,
- messages : summaryMessages
- };
-};
-
-/**
- * @constructor
- * @param {jasmine.Env} env
- * @param actual
- * @param {jasmine.Spec} spec
- */
-jasmine.Matchers = function(env, actual, spec, opt_isNot) {
- this.env = env;
- this.actual = actual;
- this.spec = spec;
- this.isNot = opt_isNot || false;
- this.reportWasCalled_ = false;
-};
-
-// todo: @deprecated as of Jasmine 0.11, remove soon [xw]
-jasmine.Matchers.pp = function(str) {
- throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
-};
-
-// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
-jasmine.Matchers.prototype.report = function(result, failing_message, details) {
- throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
-};
-
-jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
- for (var methodName in prototype) {
- if (methodName == 'report') continue;
- var orig = prototype[methodName];
- matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
- }
-};
-
-jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
- return function() {
- var matcherArgs = jasmine.util.argsToArray(arguments);
- var result = matcherFunction.apply(this, arguments);
-
- if (this.isNot) {
- result = !result;
- }
-
- if (this.reportWasCalled_) return result;
-
- var message;
- if (!result) {
- if (this.message) {
- message = this.message.apply(this, arguments);
- if (jasmine.isArray_(message)) {
- message = message[this.isNot ? 1 : 0];
- }
- } else {
- var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
- message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
- if (matcherArgs.length > 0) {
- for (var i = 0; i < matcherArgs.length; i++) {
- if (i > 0) message += ",";
- message += " " + jasmine.pp(matcherArgs[i]);
- }
- }
- message += ".";
- }
- }
- var expectationResult = new jasmine.ExpectationResult({
- matcherName: matcherName,
- passed: result,
- expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
- actual: this.actual,
- message: message
- });
- this.spec.addMatcherResult(expectationResult);
- return jasmine.undefined;
- };
-};
-
-
-
-
-/**
- * toBe: compares the actual to the expected using ===
- * @param expected
- */
-jasmine.Matchers.prototype.toBe = function(expected) {
- return this.actual === expected;
-};
-
-/**
- * toNotBe: compares the actual to the expected using !==
- * @param expected
- * @deprecated as of 1.0. Use not.toBe() instead.
- */
-jasmine.Matchers.prototype.toNotBe = function(expected) {
- return this.actual !== expected;
-};
-
-/**
- * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
- *
- * @param expected
- */
-jasmine.Matchers.prototype.toEqual = function(expected) {
- return this.env.equals_(this.actual, expected);
-};
-
-/**
- * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
- * @param expected
- * @deprecated as of 1.0. Use not.toEqual() instead.
- */
-jasmine.Matchers.prototype.toNotEqual = function(expected) {
- return !this.env.equals_(this.actual, expected);
-};
-
-/**
- * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes
- * a pattern or a String.
- *
- * @param expected
- */
-jasmine.Matchers.prototype.toMatch = function(expected) {
- return new RegExp(expected).test(this.actual);
-};
-
-/**
- * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
- * @param expected
- * @deprecated as of 1.0. Use not.toMatch() instead.
- */
-jasmine.Matchers.prototype.toNotMatch = function(expected) {
- return !(new RegExp(expected).test(this.actual));
-};
-
-/**
- * Matcher that compares the actual to jasmine.undefined.
- */
-jasmine.Matchers.prototype.toBeDefined = function() {
- return (this.actual !== jasmine.undefined);
-};
-
-/**
- * Matcher that compares the actual to jasmine.undefined.
- */
-jasmine.Matchers.prototype.toBeUndefined = function() {
- return (this.actual === jasmine.undefined);
-};
-
-/**
- * Matcher that compares the actual to null.
- */
-jasmine.Matchers.prototype.toBeNull = function() {
- return (this.actual === null);
-};
-
-/**
- * Matcher that compares the actual to NaN.
- */
-jasmine.Matchers.prototype.toBeNaN = function() {
- this.message = function() {
- return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ];
- };
-
- return (this.actual !== this.actual);
-};
-
-/**
- * Matcher that boolean not-nots the actual.
- */
-jasmine.Matchers.prototype.toBeTruthy = function() {
- return !!this.actual;
-};
-
-
-/**
- * Matcher that boolean nots the actual.
- */
-jasmine.Matchers.prototype.toBeFalsy = function() {
- return !this.actual;
-};
-
-
-/**
- * Matcher that checks to see if the actual, a Jasmine spy, was called.
- */
-jasmine.Matchers.prototype.toHaveBeenCalled = function() {
- if (arguments.length > 0) {
- throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
- }
-
- if (!jasmine.isSpy(this.actual)) {
- throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
- }
-
- this.message = function() {
- return [
- "Expected spy " + this.actual.identity + " to have been called.",
- "Expected spy " + this.actual.identity + " not to have been called."
- ];
- };
-
- return this.actual.wasCalled;
-};
-
-/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
-jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
-
-/**
- * Matcher that checks to see if the actual, a Jasmine spy, was not called.
- *
- * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
- */
-jasmine.Matchers.prototype.wasNotCalled = function() {
- if (arguments.length > 0) {
- throw new Error('wasNotCalled does not take arguments');
- }
-
- if (!jasmine.isSpy(this.actual)) {
- throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
- }
-
- this.message = function() {
- return [
- "Expected spy " + this.actual.identity + " to not have been called.",
- "Expected spy " + this.actual.identity + " to have been called."
- ];
- };
-
- return !this.actual.wasCalled;
-};
-
-/**
- * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
- *
- * @example
- *
- */
-jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
- var expectedArgs = jasmine.util.argsToArray(arguments);
- if (!jasmine.isSpy(this.actual)) {
- throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
- }
- this.message = function() {
- var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was.";
- var positiveMessage = "";
- if (this.actual.callCount === 0) {
- positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.";
- } else {
- positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but actual calls were " + jasmine.pp(this.actual.argsForCall).replace(/^\[ | \]$/g, '')
- }
- return [positiveMessage, invertedMessage];
- };
-
- return this.env.contains_(this.actual.argsForCall, expectedArgs);
-};
-
-/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
-jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
-
-/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
-jasmine.Matchers.prototype.wasNotCalledWith = function() {
- var expectedArgs = jasmine.util.argsToArray(arguments);
- if (!jasmine.isSpy(this.actual)) {
- throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
- }
-
- this.message = function() {
- return [
- "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
- "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
- ];
- };
-
- return !this.env.contains_(this.actual.argsForCall, expectedArgs);
-};
-
-/**
- * Matcher that checks that the expected item is an element in the actual Array.
- *
- * @param {Object} expected
- */
-jasmine.Matchers.prototype.toContain = function(expected) {
- return this.env.contains_(this.actual, expected);
-};
-
-/**
- * Matcher that checks that the expected item is NOT an element in the actual Array.
- *
- * @param {Object} expected
- * @deprecated as of 1.0. Use not.toContain() instead.
- */
-jasmine.Matchers.prototype.toNotContain = function(expected) {
- return !this.env.contains_(this.actual, expected);
-};
-
-jasmine.Matchers.prototype.toBeLessThan = function(expected) {
- return this.actual < expected;
-};
-
-jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
- return this.actual > expected;
-};
-
-/**
- * Matcher that checks that the expected item is equal to the actual item
- * up to a given level of decimal precision (default 2).
- *
- * @param {Number} expected
- * @param {Number} precision, as number of decimal places
- */
-jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
- if (!(precision === 0)) {
- precision = precision || 2;
- }
- return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2);
-};
-
-/**
- * Matcher that checks that the expected exception was thrown by the actual.
- *
- * @param {String} [expected]
- */
-jasmine.Matchers.prototype.toThrow = function(expected) {
- var result = false;
- var exception;
- if (typeof this.actual != 'function') {
- throw new Error('Actual is not a function');
- }
- try {
- this.actual();
- } catch (e) {
- exception = e;
- }
- if (exception) {
- result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
- }
-
- var not = this.isNot ? "not " : "";
-
- this.message = function() {
- if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
- return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
- } else {
- return "Expected function to throw an exception.";
- }
- };
-
- return result;
-};
-
-jasmine.Matchers.Any = function(expectedClass) {
- this.expectedClass = expectedClass;
-};
-
-jasmine.Matchers.Any.prototype.jasmineMatches = function(other) {
- if (this.expectedClass == String) {
- return typeof other == 'string' || other instanceof String;
- }
-
- if (this.expectedClass == Number) {
- return typeof other == 'number' || other instanceof Number;
- }
-
- if (this.expectedClass == Function) {
- return typeof other == 'function' || other instanceof Function;
- }
-
- if (this.expectedClass == Object) {
- return typeof other == 'object';
- }
-
- return other instanceof this.expectedClass;
-};
-
-jasmine.Matchers.Any.prototype.jasmineToString = function() {
- return '';
-};
-
-jasmine.Matchers.ObjectContaining = function (sample) {
- this.sample = sample;
-};
-
-jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
- mismatchKeys = mismatchKeys || [];
- mismatchValues = mismatchValues || [];
-
- var env = jasmine.getEnv();
-
- var hasKey = function(obj, keyName) {
- return obj != null && obj[keyName] !== jasmine.undefined;
- };
-
- for (var property in this.sample) {
- if (!hasKey(other, property) && hasKey(this.sample, property)) {
- mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
- }
- else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
- mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
- }
- }
-
- return (mismatchKeys.length === 0 && mismatchValues.length === 0);
-};
-
-jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {
- return "";
-};
-// Mock setTimeout, clearTimeout
-// Contributed by Pivotal Computer Systems, www.pivotalsf.com
-
-jasmine.FakeTimer = function() {
- this.reset();
-
- var self = this;
- self.setTimeout = function(funcToCall, millis) {
- self.timeoutsMade++;
- self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
- return self.timeoutsMade;
- };
-
- self.setInterval = function(funcToCall, millis) {
- self.timeoutsMade++;
- self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
- return self.timeoutsMade;
- };
-
- self.clearTimeout = function(timeoutKey) {
- self.scheduledFunctions[timeoutKey] = jasmine.undefined;
- };
-
- self.clearInterval = function(timeoutKey) {
- self.scheduledFunctions[timeoutKey] = jasmine.undefined;
- };
-
-};
-
-jasmine.FakeTimer.prototype.reset = function() {
- this.timeoutsMade = 0;
- this.scheduledFunctions = {};
- this.nowMillis = 0;
-};
-
-jasmine.FakeTimer.prototype.tick = function(millis) {
- var oldMillis = this.nowMillis;
- var newMillis = oldMillis + millis;
- this.runFunctionsWithinRange(oldMillis, newMillis);
- this.nowMillis = newMillis;
-};
-
-jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
- var scheduledFunc;
- var funcsToRun = [];
- for (var timeoutKey in this.scheduledFunctions) {
- scheduledFunc = this.scheduledFunctions[timeoutKey];
- if (scheduledFunc != jasmine.undefined &&
- scheduledFunc.runAtMillis >= oldMillis &&
- scheduledFunc.runAtMillis <= nowMillis) {
- funcsToRun.push(scheduledFunc);
- this.scheduledFunctions[timeoutKey] = jasmine.undefined;
- }
- }
-
- if (funcsToRun.length > 0) {
- funcsToRun.sort(function(a, b) {
- return a.runAtMillis - b.runAtMillis;
- });
- for (var i = 0; i < funcsToRun.length; ++i) {
- try {
- var funcToRun = funcsToRun[i];
- this.nowMillis = funcToRun.runAtMillis;
- funcToRun.funcToCall();
- if (funcToRun.recurring) {
- this.scheduleFunction(funcToRun.timeoutKey,
- funcToRun.funcToCall,
- funcToRun.millis,
- true);
- }
- } catch(e) {
- }
- }
- this.runFunctionsWithinRange(oldMillis, nowMillis);
- }
-};
-
-jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
- this.scheduledFunctions[timeoutKey] = {
- runAtMillis: this.nowMillis + millis,
- funcToCall: funcToCall,
- recurring: recurring,
- timeoutKey: timeoutKey,
- millis: millis
- };
-};
-
-/**
- * @namespace
- */
-jasmine.Clock = {
- defaultFakeTimer: new jasmine.FakeTimer(),
-
- reset: function() {
- jasmine.Clock.assertInstalled();
- jasmine.Clock.defaultFakeTimer.reset();
- },
-
- tick: function(millis) {
- jasmine.Clock.assertInstalled();
- jasmine.Clock.defaultFakeTimer.tick(millis);
- },
-
- runFunctionsWithinRange: function(oldMillis, nowMillis) {
- jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
- },
-
- scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
- jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
- },
-
- useMock: function() {
- if (!jasmine.Clock.isInstalled()) {
- var spec = jasmine.getEnv().currentSpec;
- spec.after(jasmine.Clock.uninstallMock);
-
- jasmine.Clock.installMock();
- }
- },
-
- installMock: function() {
- jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
- },
-
- uninstallMock: function() {
- jasmine.Clock.assertInstalled();
- jasmine.Clock.installed = jasmine.Clock.real;
- },
-
- real: {
- setTimeout: jasmine.getGlobal().setTimeout,
- clearTimeout: jasmine.getGlobal().clearTimeout,
- setInterval: jasmine.getGlobal().setInterval,
- clearInterval: jasmine.getGlobal().clearInterval
- },
-
- assertInstalled: function() {
- if (!jasmine.Clock.isInstalled()) {
- throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
- }
- },
-
- isInstalled: function() {
- return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
- },
-
- installed: null
-};
-jasmine.Clock.installed = jasmine.Clock.real;
-
-//else for IE support
-jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
- if (jasmine.Clock.installed.setTimeout.apply) {
- return jasmine.Clock.installed.setTimeout.apply(this, arguments);
- } else {
- return jasmine.Clock.installed.setTimeout(funcToCall, millis);
- }
-};
-
-jasmine.getGlobal().setInterval = function(funcToCall, millis) {
- if (jasmine.Clock.installed.setInterval.apply) {
- return jasmine.Clock.installed.setInterval.apply(this, arguments);
- } else {
- return jasmine.Clock.installed.setInterval(funcToCall, millis);
- }
-};
-
-jasmine.getGlobal().clearTimeout = function(timeoutKey) {
- if (jasmine.Clock.installed.clearTimeout.apply) {
- return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
- } else {
- return jasmine.Clock.installed.clearTimeout(timeoutKey);
- }
-};
-
-jasmine.getGlobal().clearInterval = function(timeoutKey) {
- if (jasmine.Clock.installed.clearTimeout.apply) {
- return jasmine.Clock.installed.clearInterval.apply(this, arguments);
- } else {
- return jasmine.Clock.installed.clearInterval(timeoutKey);
- }
-};
-
-/**
- * @constructor
- */
-jasmine.MultiReporter = function() {
- this.subReporters_ = [];
-};
-jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
-
-jasmine.MultiReporter.prototype.addReporter = function(reporter) {
- this.subReporters_.push(reporter);
-};
-
-(function() {
- var functionNames = [
- "reportRunnerStarting",
- "reportRunnerResults",
- "reportSuiteResults",
- "reportSpecStarting",
- "reportSpecResults",
- "log"
- ];
- for (var i = 0; i < functionNames.length; i++) {
- var functionName = functionNames[i];
- jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
- return function() {
- for (var j = 0; j < this.subReporters_.length; j++) {
- var subReporter = this.subReporters_[j];
- if (subReporter[functionName]) {
- subReporter[functionName].apply(subReporter, arguments);
- }
- }
- };
- })(functionName);
- }
-})();
-/**
- * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
- *
- * @constructor
- */
-jasmine.NestedResults = function() {
- /**
- * The total count of results
- */
- this.totalCount = 0;
- /**
- * Number of passed results
- */
- this.passedCount = 0;
- /**
- * Number of failed results
- */
- this.failedCount = 0;
- /**
- * Was this suite/spec skipped?
- */
- this.skipped = false;
- /**
- * @ignore
- */
- this.items_ = [];
-};
-
-/**
- * Roll up the result counts.
- *
- * @param result
- */
-jasmine.NestedResults.prototype.rollupCounts = function(result) {
- this.totalCount += result.totalCount;
- this.passedCount += result.passedCount;
- this.failedCount += result.failedCount;
-};
-
-/**
- * Adds a log message.
- * @param values Array of message parts which will be concatenated later.
- */
-jasmine.NestedResults.prototype.log = function(values) {
- this.items_.push(new jasmine.MessageResult(values));
-};
-
-/**
- * Getter for the results: message & results.
- */
-jasmine.NestedResults.prototype.getItems = function() {
- return this.items_;
-};
-
-/**
- * Adds a result, tracking counts (total, passed, & failed)
- * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
- */
-jasmine.NestedResults.prototype.addResult = function(result) {
- if (result.type != 'log') {
- if (result.items_) {
- this.rollupCounts(result);
- } else {
- this.totalCount++;
- if (result.passed()) {
- this.passedCount++;
- } else {
- this.failedCount++;
- }
- }
- }
- this.items_.push(result);
-};
-
-/**
- * @returns {Boolean} True if everything below passed
- */
-jasmine.NestedResults.prototype.passed = function() {
- return this.passedCount === this.totalCount;
-};
-/**
- * Base class for pretty printing for expectation results.
- */
-jasmine.PrettyPrinter = function() {
- this.ppNestLevel_ = 0;
-};
-
-/**
- * Formats a value in a nice, human-readable string.
- *
- * @param value
- */
-jasmine.PrettyPrinter.prototype.format = function(value) {
- this.ppNestLevel_++;
- try {
- if (value === jasmine.undefined) {
- this.emitScalar('undefined');
- } else if (value === null) {
- this.emitScalar('null');
- } else if (value === jasmine.getGlobal()) {
- this.emitScalar('');
- } else if (value.jasmineToString) {
- this.emitScalar(value.jasmineToString());
- } else if (typeof value === 'string') {
- this.emitString(value);
- } else if (jasmine.isSpy(value)) {
- this.emitScalar("spy on " + value.identity);
- } else if (value instanceof RegExp) {
- this.emitScalar(value.toString());
- } else if (typeof value === 'function') {
- this.emitScalar('Function');
- } else if (typeof value.nodeType === 'number') {
- this.emitScalar('HTMLNode');
- } else if (value instanceof Date) {
- this.emitScalar('Date(' + value + ')');
- } else if (value.__Jasmine_been_here_before__) {
- this.emitScalar('');
- } else if (jasmine.isArray_(value) || typeof value == 'object') {
- value.__Jasmine_been_here_before__ = true;
- if (jasmine.isArray_(value)) {
- this.emitArray(value);
- } else {
- this.emitObject(value);
- }
- delete value.__Jasmine_been_here_before__;
- } else {
- this.emitScalar(value.toString());
- }
- } finally {
- this.ppNestLevel_--;
- }
-};
-
-jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
- for (var property in obj) {
- if (!obj.hasOwnProperty(property)) continue;
- if (property == '__Jasmine_been_here_before__') continue;
- fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined &&
- obj.__lookupGetter__(property) !== null) : false);
- }
-};
-
-jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
-jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
-jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
-jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
-
-jasmine.StringPrettyPrinter = function() {
- jasmine.PrettyPrinter.call(this);
-
- this.string = '';
-};
-jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
-
-jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
- this.append(value);
-};
-
-jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
- this.append("'" + value + "'");
-};
-
-jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
- if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {
- this.append("Array");
- return;
- }
-
- this.append('[ ');
- for (var i = 0; i < array.length; i++) {
- if (i > 0) {
- this.append(', ');
- }
- this.format(array[i]);
- }
- this.append(' ]');
-};
-
-jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
- if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {
- this.append("Object");
- return;
- }
-
- var self = this;
- this.append('{ ');
- var first = true;
-
- this.iterateObject(obj, function(property, isGetter) {
- if (first) {
- first = false;
- } else {
- self.append(', ');
- }
-
- self.append(property);
- self.append(' : ');
- if (isGetter) {
- self.append('');
- } else {
- self.format(obj[property]);
- }
- });
-
- this.append(' }');
-};
-
-jasmine.StringPrettyPrinter.prototype.append = function(value) {
- this.string += value;
-};
-jasmine.Queue = function(env) {
- this.env = env;
-
- // parallel to blocks. each true value in this array means the block will
- // get executed even if we abort
- this.ensured = [];
- this.blocks = [];
- this.running = false;
- this.index = 0;
- this.offset = 0;
- this.abort = false;
-};
-
-jasmine.Queue.prototype.addBefore = function(block, ensure) {
- if (ensure === jasmine.undefined) {
- ensure = false;
- }
-
- this.blocks.unshift(block);
- this.ensured.unshift(ensure);
-};
-
-jasmine.Queue.prototype.add = function(block, ensure) {
- if (ensure === jasmine.undefined) {
- ensure = false;
- }
-
- this.blocks.push(block);
- this.ensured.push(ensure);
-};
-
-jasmine.Queue.prototype.insertNext = function(block, ensure) {
- if (ensure === jasmine.undefined) {
- ensure = false;
- }
-
- this.ensured.splice((this.index + this.offset + 1), 0, ensure);
- this.blocks.splice((this.index + this.offset + 1), 0, block);
- this.offset++;
-};
-
-jasmine.Queue.prototype.start = function(onComplete) {
- this.running = true;
- this.onComplete = onComplete;
- this.next_();
-};
-
-jasmine.Queue.prototype.isRunning = function() {
- return this.running;
-};
-
-jasmine.Queue.LOOP_DONT_RECURSE = true;
-
-jasmine.Queue.prototype.next_ = function() {
- var self = this;
- var goAgain = true;
-
- while (goAgain) {
- goAgain = false;
-
- if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) {
- var calledSynchronously = true;
- var completedSynchronously = false;
-
- var onComplete = function () {
- if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
- completedSynchronously = true;
- return;
- }
-
- if (self.blocks[self.index].abort) {
- self.abort = true;
- }
-
- self.offset = 0;
- self.index++;
-
- var now = new Date().getTime();
- if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
- self.env.lastUpdate = now;
- self.env.setTimeout(function() {
- self.next_();
- }, 0);
- } else {
- if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
- goAgain = true;
- } else {
- self.next_();
- }
- }
- };
- self.blocks[self.index].execute(onComplete);
-
- calledSynchronously = false;
- if (completedSynchronously) {
- onComplete();
- }
-
- } else {
- self.running = false;
- if (self.onComplete) {
- self.onComplete();
- }
- }
- }
-};
-
-jasmine.Queue.prototype.results = function() {
- var results = new jasmine.NestedResults();
- for (var i = 0; i < this.blocks.length; i++) {
- if (this.blocks[i].results) {
- results.addResult(this.blocks[i].results());
- }
- }
- return results;
-};
-
-
-/**
- * Runner
- *
- * @constructor
- * @param {jasmine.Env} env
- */
-jasmine.Runner = function(env) {
- var self = this;
- self.env = env;
- self.queue = new jasmine.Queue(env);
- self.before_ = [];
- self.after_ = [];
- self.suites_ = [];
-};
-
-jasmine.Runner.prototype.execute = function() {
- var self = this;
- if (self.env.reporter.reportRunnerStarting) {
- self.env.reporter.reportRunnerStarting(this);
- }
- self.queue.start(function () {
- self.finishCallback();
- });
-};
-
-jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
- beforeEachFunction.typeName = 'beforeEach';
- this.before_.splice(0,0,beforeEachFunction);
-};
-
-jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
- afterEachFunction.typeName = 'afterEach';
- this.after_.splice(0,0,afterEachFunction);
-};
-
-
-jasmine.Runner.prototype.finishCallback = function() {
- this.env.reporter.reportRunnerResults(this);
-};
-
-jasmine.Runner.prototype.addSuite = function(suite) {
- this.suites_.push(suite);
-};
-
-jasmine.Runner.prototype.add = function(block) {
- if (block instanceof jasmine.Suite) {
- this.addSuite(block);
- }
- this.queue.add(block);
-};
-
-jasmine.Runner.prototype.specs = function () {
- var suites = this.suites();
- var specs = [];
- for (var i = 0; i < suites.length; i++) {
- specs = specs.concat(suites[i].specs());
- }
- return specs;
-};
-
-jasmine.Runner.prototype.suites = function() {
- return this.suites_;
-};
-
-jasmine.Runner.prototype.topLevelSuites = function() {
- var topLevelSuites = [];
- for (var i = 0; i < this.suites_.length; i++) {
- if (!this.suites_[i].parentSuite) {
- topLevelSuites.push(this.suites_[i]);
- }
- }
- return topLevelSuites;
-};
-
-jasmine.Runner.prototype.results = function() {
- return this.queue.results();
-};
-/**
- * Internal representation of a Jasmine specification, or test.
- *
- * @constructor
- * @param {jasmine.Env} env
- * @param {jasmine.Suite} suite
- * @param {String} description
- */
-jasmine.Spec = function(env, suite, description) {
- if (!env) {
- throw new Error('jasmine.Env() required');
- }
- if (!suite) {
- throw new Error('jasmine.Suite() required');
- }
- var spec = this;
- spec.id = env.nextSpecId ? env.nextSpecId() : null;
- spec.env = env;
- spec.suite = suite;
- spec.description = description;
- spec.queue = new jasmine.Queue(env);
-
- spec.afterCallbacks = [];
- spec.spies_ = [];
-
- spec.results_ = new jasmine.NestedResults();
- spec.results_.description = description;
- spec.matchersClass = null;
-};
-
-jasmine.Spec.prototype.getFullName = function() {
- return this.suite.getFullName() + ' ' + this.description + '.';
-};
-
-
-jasmine.Spec.prototype.results = function() {
- return this.results_;
-};
-
-/**
- * All parameters are pretty-printed and concatenated together, then written to the spec's output.
- *
- * Be careful not to leave calls to jasmine.log in production code.
- */
-jasmine.Spec.prototype.log = function() {
- return this.results_.log(arguments);
-};
-
-jasmine.Spec.prototype.runs = function (func) {
- var block = new jasmine.Block(this.env, func, this);
- this.addToQueue(block);
- return this;
-};
-
-jasmine.Spec.prototype.addToQueue = function (block) {
- if (this.queue.isRunning()) {
- this.queue.insertNext(block);
- } else {
- this.queue.add(block);
- }
-};
-
-/**
- * @param {jasmine.ExpectationResult} result
- */
-jasmine.Spec.prototype.addMatcherResult = function(result) {
- this.results_.addResult(result);
-};
-
-jasmine.Spec.prototype.expect = function(actual) {
- var positive = new (this.getMatchersClass_())(this.env, actual, this);
- positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
- return positive;
-};
-
-/**
- * Waits a fixed time period before moving to the next block.
- *
- * @deprecated Use waitsFor() instead
- * @param {Number} timeout milliseconds to wait
- */
-jasmine.Spec.prototype.waits = function(timeout) {
- var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
- this.addToQueue(waitsFunc);
- return this;
-};
-
-/**
- * Waits for the latchFunction to return true before proceeding to the next block.
- *
- * @param {Function} latchFunction
- * @param {String} optional_timeoutMessage
- * @param {Number} optional_timeout
- */
-jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
- var latchFunction_ = null;
- var optional_timeoutMessage_ = null;
- var optional_timeout_ = null;
-
- for (var i = 0; i < arguments.length; i++) {
- var arg = arguments[i];
- switch (typeof arg) {
- case 'function':
- latchFunction_ = arg;
- break;
- case 'string':
- optional_timeoutMessage_ = arg;
- break;
- case 'number':
- optional_timeout_ = arg;
- break;
- }
- }
-
- var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
- this.addToQueue(waitsForFunc);
- return this;
-};
-
-jasmine.Spec.prototype.fail = function (e) {
- var expectationResult = new jasmine.ExpectationResult({
- passed: false,
- message: e ? jasmine.util.formatException(e) : 'Exception',
- trace: { stack: e.stack }
- });
- this.results_.addResult(expectationResult);
-};
-
-jasmine.Spec.prototype.getMatchersClass_ = function() {
- return this.matchersClass || this.env.matchersClass;
-};
-
-jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
- var parent = this.getMatchersClass_();
- var newMatchersClass = function() {
- parent.apply(this, arguments);
- };
- jasmine.util.inherit(newMatchersClass, parent);
- jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
- this.matchersClass = newMatchersClass;
-};
-
-jasmine.Spec.prototype.finishCallback = function() {
- this.env.reporter.reportSpecResults(this);
-};
-
-jasmine.Spec.prototype.finish = function(onComplete) {
- this.removeAllSpies();
- this.finishCallback();
- if (onComplete) {
- onComplete();
- }
-};
-
-jasmine.Spec.prototype.after = function(doAfter) {
- if (this.queue.isRunning()) {
- this.queue.add(new jasmine.Block(this.env, doAfter, this), true);
- } else {
- this.afterCallbacks.unshift(doAfter);
- }
-};
-
-jasmine.Spec.prototype.execute = function(onComplete) {
- var spec = this;
- if (!spec.env.specFilter(spec)) {
- spec.results_.skipped = true;
- spec.finish(onComplete);
- return;
- }
-
- this.env.reporter.reportSpecStarting(this);
-
- spec.env.currentSpec = spec;
-
- spec.addBeforesAndAftersToQueue();
-
- spec.queue.start(function () {
- spec.finish(onComplete);
- });
-};
-
-jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
- var runner = this.env.currentRunner();
- var i;
-
- for (var suite = this.suite; suite; suite = suite.parentSuite) {
- for (i = 0; i < suite.before_.length; i++) {
- this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
- }
- }
- for (i = 0; i < runner.before_.length; i++) {
- this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
- }
- for (i = 0; i < this.afterCallbacks.length; i++) {
- this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true);
- }
- for (suite = this.suite; suite; suite = suite.parentSuite) {
- for (i = 0; i < suite.after_.length; i++) {
- this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true);
- }
- }
- for (i = 0; i < runner.after_.length; i++) {
- this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true);
- }
-};
-
-jasmine.Spec.prototype.explodes = function() {
- throw 'explodes function should not have been called';
-};
-
-jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
- if (obj == jasmine.undefined) {
- throw "spyOn could not find an object to spy upon for " + methodName + "()";
- }
-
- if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
- throw methodName + '() method does not exist';
- }
-
- if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
- throw new Error(methodName + ' has already been spied upon');
- }
-
- var spyObj = jasmine.createSpy(methodName);
-
- this.spies_.push(spyObj);
- spyObj.baseObj = obj;
- spyObj.methodName = methodName;
- spyObj.originalValue = obj[methodName];
-
- obj[methodName] = spyObj;
-
- return spyObj;
-};
-
-jasmine.Spec.prototype.removeAllSpies = function() {
- for (var i = 0; i < this.spies_.length; i++) {
- var spy = this.spies_[i];
- spy.baseObj[spy.methodName] = spy.originalValue;
- }
- this.spies_ = [];
-};
-
-/**
- * Internal representation of a Jasmine suite.
- *
- * @constructor
- * @param {jasmine.Env} env
- * @param {String} description
- * @param {Function} specDefinitions
- * @param {jasmine.Suite} parentSuite
- */
-jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
- var self = this;
- self.id = env.nextSuiteId ? env.nextSuiteId() : null;
- self.description = description;
- self.queue = new jasmine.Queue(env);
- self.parentSuite = parentSuite;
- self.env = env;
- self.before_ = [];
- self.after_ = [];
- self.children_ = [];
- self.suites_ = [];
- self.specs_ = [];
-};
-
-jasmine.Suite.prototype.getFullName = function() {
- var fullName = this.description;
- for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
- fullName = parentSuite.description + ' ' + fullName;
- }
- return fullName;
-};
-
-jasmine.Suite.prototype.finish = function(onComplete) {
- this.env.reporter.reportSuiteResults(this);
- this.finished = true;
- if (typeof(onComplete) == 'function') {
- onComplete();
- }
-};
-
-jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
- beforeEachFunction.typeName = 'beforeEach';
- this.before_.unshift(beforeEachFunction);
-};
-
-jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
- afterEachFunction.typeName = 'afterEach';
- this.after_.unshift(afterEachFunction);
-};
-
-jasmine.Suite.prototype.results = function() {
- return this.queue.results();
-};
-
-jasmine.Suite.prototype.add = function(suiteOrSpec) {
- this.children_.push(suiteOrSpec);
- if (suiteOrSpec instanceof jasmine.Suite) {
- this.suites_.push(suiteOrSpec);
- this.env.currentRunner().addSuite(suiteOrSpec);
- } else {
- this.specs_.push(suiteOrSpec);
- }
- this.queue.add(suiteOrSpec);
-};
-
-jasmine.Suite.prototype.specs = function() {
- return this.specs_;
-};
-
-jasmine.Suite.prototype.suites = function() {
- return this.suites_;
-};
-
-jasmine.Suite.prototype.children = function() {
- return this.children_;
-};
-
-jasmine.Suite.prototype.execute = function(onComplete) {
- var self = this;
- this.queue.start(function () {
- self.finish(onComplete);
- });
-};
-jasmine.WaitsBlock = function(env, timeout, spec) {
- this.timeout = timeout;
- jasmine.Block.call(this, env, null, spec);
-};
-
-jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
-
-jasmine.WaitsBlock.prototype.execute = function (onComplete) {
- if (jasmine.VERBOSE) {
- this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
- }
- this.env.setTimeout(function () {
- onComplete();
- }, this.timeout);
-};
-/**
- * A block which waits for some condition to become true, with timeout.
- *
- * @constructor
- * @extends jasmine.Block
- * @param {jasmine.Env} env The Jasmine environment.
- * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
- * @param {Function} latchFunction A function which returns true when the desired condition has been met.
- * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
- * @param {jasmine.Spec} spec The Jasmine spec.
- */
-jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
- this.timeout = timeout || env.defaultTimeoutInterval;
- this.latchFunction = latchFunction;
- this.message = message;
- this.totalTimeSpentWaitingForLatch = 0;
- jasmine.Block.call(this, env, null, spec);
-};
-jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
-
-jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
-
-jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
- if (jasmine.VERBOSE) {
- this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
- }
- var latchFunctionResult;
- try {
- latchFunctionResult = this.latchFunction.apply(this.spec);
- } catch (e) {
- this.spec.fail(e);
- onComplete();
- return;
- }
-
- if (latchFunctionResult) {
- onComplete();
- } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
- var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
- this.spec.fail({
- name: 'timeout',
- message: message
- });
-
- this.abort = true;
- onComplete();
- } else {
- this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
- var self = this;
- this.env.setTimeout(function() {
- self.execute(onComplete);
- }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
- }
-};
-
-jasmine.version_= {
- "major": 1,
- "minor": 3,
- "build": 1,
- "revision": 1354556913
-};
diff --git a/src/AttributeRouting.Tests/Integration/js/lib/jquery-1.9.1.min.js b/src/AttributeRouting.Tests/Integration/js/lib/jquery-1.9.1.min.js
deleted file mode 100644
index 39aae4e..0000000
--- a/src/AttributeRouting.Tests/Integration/js/lib/jquery-1.9.1.min.js
+++ /dev/null
@@ -1,5 +0,0 @@
-/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license
-//@ sourceMappingURL=jquery.min.map
-*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML=" a",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;
-return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,""],area:[1,""],param:[1,"",""],thead:[1,""],tr:[2,""],col:[2,""],td:[3,""],_default:b.support.htmlSerialize?[0,"",""]:[1,"X","
"]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>$2>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l)
-}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write(""),t.close(),n=ln(e,t),Pt.detach()),Gt[e]=n),n}function ln(e,t){var n=b(t.createElement(e)).appendTo(t.body),r=b.css(n[0],"display");return n.remove(),r}b.each(["height","width"],function(e,n){b.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(b.css(e,"display"))?b.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,i),i):0)}}}),b.support.opacity||(b.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=b.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===b.trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),b(function(){b.support.reliableMarginRight||(b.cssHooks.marginRight={get:function(e,n){return n?b.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!b.support.pixelPosition&&b.fn.position&&b.each(["top","left"],function(e,n){b.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?b(e).position()[n]+"px":r):t}}})}),b.expr&&b.expr.filters&&(b.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!b.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||b.css(e,"display"))},b.expr.filters.visible=function(e){return!b.expr.filters.hidden(e)}),b.each({margin:"",padding:"",border:"Width"},function(e,t){b.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(b.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=b.prop(this,"elements");return e?b.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!b(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Nt.test(e))}).map(function(e,t){var n=b(this).val();return null==n?null:b.isArray(n)?b.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),b.param=function(e,n){var r,i=[],o=function(e,t){t=b.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=b.ajaxSettings&&b.ajaxSettings.traditional),b.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(cn,"+")};function gn(e,t,n,r){var i;if(b.isArray(t))b.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==b.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}b.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){b.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),b.fn.hover=function(e,t){return this.mouseenter(e).mouseleave(t||e)};var mn,yn,vn=b.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Nn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Cn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=b.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=a.href}catch(Ln){yn=o.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(w)||[];if(b.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(u){var l;return o[u]=!0,b.each(e[u]||[],function(e,u){var c=u(n,r,i);return"string"!=typeof c||a||o[c]?a?!(l=c):t:(n.dataTypes.unshift(c),s(c),!1)}),l}return s(n.dataTypes[0])||!o["*"]&&s("*")}function Mn(e,n){var r,i,o=b.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&b.extend(!0,e,r),e}b.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(" ");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),b.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&b.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?b("").append(b.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},b.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){b.fn[t]=function(e){return this.on(t,e)}}),b.each(["get","post"],function(e,n){b[n]=function(e,r,i,o){return b.isFunction(r)&&(o=o||i,i=r,r=t),b.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Nn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":b.parseJSON,"text xml":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Mn(Mn(e,b.ajaxSettings),t):Mn(b.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,u,l,c,p=b.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?b(f):b.event,h=b.Deferred(),g=b.Callbacks("once memory"),m=p.statusCode||{},y={},v={},x=0,T="canceled",N={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)m[t]=[m[t],e[t]];else N.always(e[N.status]);return this},abort:function(e){var t=e||T;return l&&l.abort(t),k(0,t),this}};if(h.promise(N).complete=g.add,N.success=N.done,N.error=N.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=b.trim(p.dataType||"*").toLowerCase().match(w)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?80:443))==(mn[3]||("http:"===mn[1]?80:443)))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=b.param(p.data,p.traditional)),qn(An,p,n,N),2===x)return N;u=p.global,u&&0===b.active++&&b.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Cn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(b.lastModified[o]&&N.setRequestHeader("If-Modified-Since",b.lastModified[o]),b.etag[o]&&N.setRequestHeader("If-None-Match",b.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&N.setRequestHeader("Content-Type",p.contentType),N.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)N.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,N,p)===!1||2===x))return N.abort();T="abort";for(i in{success:1,error:1,complete:1})N[i](p[i]);if(l=qn(jn,p,n,N)){N.readyState=1,u&&d.trigger("ajaxSend",[N,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){N.abort("timeout")},p.timeout));try{x=1,l.send(y,k)}catch(C){if(!(2>x))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,C=n;2!==x&&(x=2,s&&clearTimeout(s),l=t,a=i||"",N.readyState=e>0?4:0,r&&(w=_n(p,N,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=N.getResponseHeader("Last-Modified"),T&&(b.lastModified[o]=T),T=N.getResponseHeader("etag"),T&&(b.etag[o]=T)),204===e?(c=!0,C="nocontent"):304===e?(c=!0,C="notmodified"):(c=Fn(p,w),C=c.state,y=c.data,v=c.error,c=!v)):(v=C,(e||!C)&&(C="error",0>e&&(e=0))),N.status=e,N.statusText=(n||C)+"",c?h.resolveWith(f,[y,C,N]):h.rejectWith(f,[N,C,v]),N.statusCode(m),m=t,u&&d.trigger(c?"ajaxSuccess":"ajaxError",[N,p,c?y:v]),g.fireWith(f,[N,C]),u&&(d.trigger("ajaxComplete",[N,p]),--b.active||b.event.trigger("ajaxStop")))}return N},getScript:function(e,n){return b.get(e,t,n,"script")},getJSON:function(e,t,n){return b.get(e,t,n,"json")}});function _n(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(s in c)s in r&&(n[c[s]]=r[s]);while("*"===l[0])l.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in u)if(u[s]&&u[s].test(o)){l.unshift(s);break}if(l[0]in r)a=l[0];else{for(s in r){if(!l[0]||e.converters[s+" "+l[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function Fn(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(i in e.converters)a[i.toLowerCase()]=e.converters[i];for(;r=u[++s];)if("*"!==r){if("*"!==l&&l!==r){if(i=a[l+" "+r]||a["* "+r],!i)for(n in a)if(o=n.split(" "),o[1]===r&&(i=a[l+" "+o[0]]||a["* "+o[0]])){i===!0?i=a[n]:a[n]!==!0&&(r=o[0],u.splice(s--,0,r));break}if(i!==!0)if(i&&e["throws"])t=i(t);else try{t=i(t)}catch(c){return{state:"parsererror",error:i?c:"No conversion from "+l+" to "+r}}}l=r}return{state:"success",data:t}}b.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),b.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=o.head||b("head")[0]||o.documentElement;return{send:function(t,i){n=o.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var On=[],Bn=/(=)\?(?=&|$)|\?\?/;b.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=On.pop()||b.expando+"_"+vn++;return this[e]=!0,e}}),b.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return u||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=b.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||b.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,On.push(o)),s&&b.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}b.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=b.ajaxSettings.xhr(),b.support.cors=!!Rn&&"withCredentials"in Rn,Rn=b.support.ajax=!!Rn,Rn&&b.ajaxTransport(function(n){if(!n.crossDomain||b.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=b.noop,$n&&delete Pn[a]),i)4!==u.readyState&&u.abort();else{p={},s=u.status,l=u.getAllResponseHeaders(),"string"==typeof u.responseText&&(p.text=u.responseText);try{c=u.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,l)},n.async?4===u.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},b(e).unload($n)),Pn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+x+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=Yn.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(b.cssNumber[e]?"":"px"),"px"!==r&&s){s=b.css(i.elem,e,!0)||n||1;do u=u||".5",s/=u,b.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=b.now()}function Zn(e,t){b.each(t,function(t,n){var r=(Qn[t]||[]).concat(Qn["*"]),i=0,o=r.length;for(;o>i;i++)if(r[i].call(e,t,n))return})}function er(e,t,n){var r,i,o=0,a=Gn.length,s=b.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;for(;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:b.extend({},t),opts:b.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=b.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(tr(c,l.opts.specialEasing);a>o;o++)if(r=Gn[o].call(l,e,c,l.opts))return r;return Zn(l,c),b.isFunction(l.opts.start)&&l.opts.start.call(e,l),b.fx.timer(b.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function tr(e,t){var n,r,i,o,a;for(i in e)if(r=b.camelCase(i),o=t[r],n=e[i],b.isArray(n)&&(o=n[1],n=e[i]=n[0]),i!==r&&(e[r]=n,delete e[i]),a=b.cssHooks[r],a&&"expand"in a){n=a.expand(n),delete e[r];for(i in n)i in e||(e[i]=n[i],t[i]=o)}else t[r]=o}b.Animation=b.extend(er,{tweener:function(e,t){b.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,u,l,c,p,f=this,d=e.style,h={},g=[],m=e.nodeType&&nn(e);n.queue||(c=b._queueHooks(e,"fx"),null==c.unqueued&&(c.unqueued=0,p=c.empty.fire,c.empty.fire=function(){c.unqueued||p()}),c.unqueued++,f.always(function(){f.always(function(){c.unqueued--,b.queue(e,"fx").length||c.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],"inline"===b.css(e,"display")&&"none"===b.css(e,"float")&&(b.support.inlineBlockNeedsLayout&&"inline"!==un(e.nodeName)?d.zoom=1:d.display="inline-block")),n.overflow&&(d.overflow="hidden",b.support.shrinkWrapBlocks||f.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(i in t)if(a=t[i],Vn.exec(a)){if(delete t[i],u=u||"toggle"===a,a===(m?"hide":"show"))continue;g.push(i)}if(o=g.length){s=b._data(e,"fxshow")||b._data(e,"fxshow",{}),"hidden"in s&&(m=s.hidden),u&&(s.hidden=!m),m?b(e).show():f.done(function(){b(e).hide()}),f.done(function(){var t;b._removeData(e,"fxshow");for(t in h)b.style(e,t,h[t])});for(i=0;o>i;i++)r=g[i],l=f.createTween(r,m?s[r]:0),h[r]=s[r]||b.style(e,r),r in s||(s[r]=l.start,m&&(l.end=l.start,l.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}b.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=b.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[b.cssProps[e.prop]]||b.cssHooks[e.prop])?b.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.each(["toggle","show","hide"],function(e,t){var n=b.fn[t];b.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),b.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=b.isEmptyObject(e),o=b.speed(t,n,r),a=function(){var t=er(this,b.extend({},e),o);a.finish=function(){t.stop(!0)},(i||b._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=b.timers,a=b._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&b.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=b._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=b.timers,a=r?r.length:0;for(n.finish=!0,b.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}b.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){b.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),b.speed=function(e,t,n){var r=e&&"object"==typeof e?b.extend({},e):{complete:n||!n&&t||b.isFunction(e)&&e,duration:e,easing:n&&t||t&&!b.isFunction(t)&&t};return r.duration=b.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in b.fx.speeds?b.fx.speeds[r.duration]:b.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){b.isFunction(r.old)&&r.old.call(this),r.queue&&b.dequeue(this,r.queue)},r},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},b.timers=[],b.fx=rr.prototype.init,b.fx.tick=function(){var e,n=b.timers,r=0;for(Xn=b.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||b.fx.stop(),Xn=t},b.fx.timer=function(e){e()&&b.timers.push(e)&&b.fx.start()},b.fx.interval=13,b.fx.start=function(){Un||(Un=setInterval(b.fx.tick,b.fx.interval))},b.fx.stop=function(){clearInterval(Un),Un=null},b.fx.speeds={slow:600,fast:200,_default:400},b.fx.step={},b.expr&&b.expr.filters&&(b.expr.filters.animated=function(e){return b.grep(b.timers,function(t){return e===t.elem}).length}),b.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){b.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,b.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},b.offset={setOffset:function(e,t,n){var r=b.css(e,"position");"static"===r&&(e.style.position="relative");var i=b(e),o=i.offset(),a=b.css(e,"top"),s=b.css(e,"left"),u=("absolute"===r||"fixed"===r)&&b.inArray("auto",[a,s])>-1,l={},c={},p,f;u?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),b.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(l.top=t.top-o.top+p),null!=t.left&&(l.left=t.left-o.left+f),"using"in t?t.using.call(e,l):i.css(l)}},b.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===b.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),b.nodeName(e[0],"html")||(n=e.offset()),n.top+=b.css(e[0],"borderTopWidth",!0),n.left+=b.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-b.css(r,"marginTop",!0),left:t.left-n.left-b.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||o.documentElement;while(e&&!b.nodeName(e,"html")&&"static"===b.css(e,"position"))e=e.offsetParent;return e||o.documentElement})}}),b.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);b.fn[e]=function(i){return b.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?b(a).scrollLeft():o,r?o:b(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return b.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}b.each({Height:"height",Width:"width"},function(e,n){b.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){b.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return b.access(this,function(n,r,i){var o;return b.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?b.css(n,r,s):b.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=b,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return b})})(window);
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Integration/js/specs/basics.js b/src/AttributeRouting.Tests/Integration/js/specs/basics.js
deleted file mode 100644
index 7d1aea4..0000000
--- a/src/AttributeRouting.Tests/Integration/js/specs/basics.js
+++ /dev/null
@@ -1,17 +0,0 @@
-///
-///
-
-describe('Basic Usage', function() {
- describe('BasicsController', function () {
- it('should respond to GET "/Basics" with "Basics.Index"', specs.respondsWith);
- it('should respond to POST "/Basics" with "Basics.Create"', specs.respondsWith);
- it('should respond to PUT "/Basics/1" with "Basics.Update(1)"', specs.respondsWith);
- it('should respond to DELETE "/Basics/1" with "Basics.Delete(1)"', specs.respondsWith);
- });
- describe('HttpBasicsController', function () {
- it('should respond to GET "/HttpBasics" with "HttpBasics.Index"', specs.respondsWith);
- it('should respond to POST "/HttpBasics" with "HttpBasics.Create"', specs.respondsWith);
- it('should respond to PUT "/HttpBasics/1" with "HttpBasics.Update(1)"', specs.respondsWith);
- it('should respond to DELETE "/HttpBasics/1" with "HttpBasics.Delete(1)"', specs.respondsWith);
- });
-});
diff --git a/src/AttributeRouting.Tests/Integration/js/specs/constraints.js b/src/AttributeRouting.Tests/Integration/js/specs/constraints.js
deleted file mode 100644
index 0fa794e..0000000
--- a/src/AttributeRouting.Tests/Integration/js/specs/constraints.js
+++ /dev/null
@@ -1,76 +0,0 @@
-///
-///
-
-describe('Route Constraints', function() {
- describe('ConstraintsController', function () {
- it('should respond to GET "/Constraints/Alpha/abc" with "Constraints.Alpha(abc)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Alpha/123" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Int/123" with "Constraints.Int(123)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Int/abc" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Long/123" with "Constraints.Long(123)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Long/abc" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Float/256" with "Constraints.Float(256)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Float/abc" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Double/256" with "Constraints.Double(256)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Double/abc" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Decimal/256" with "Constraints.Decimal(256)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Decimal/abc" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Bool/true" with "Constraints.Bool(True)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Bool/false" with "Constraints.Bool(False)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Bool/0" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Guid/C568BCD6-7D2B-4241-9191-F4B1ED4E1632" with "Constraints.Guid(C568BCD6-7D2B-4241-9191-F4B1ED4E1632)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Guid/0" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/DateTime/2013-01-01" with "Constraints.DateTime(1/1/2013 12:00:00 am)"', specs.respondsWith);
- it('should respond to GET "/Constraints/DateTime/2013-9" with "Constraints.DateTime(9/1/2013 12:00:00 am)"', specs.respondsWith);
- it('should respond to GET "/Constraints/DateTime/January" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Length/a" with "Constraints.Length(a)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Length/ab" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/MinLength/abcde" with "Constraints.MinLength(abcde)"', specs.respondsWith);
- it('should respond to GET "/Constraints/MinLength/abcd" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/MaxLength/abcde" with "Constraints.MaxLength(abcde)"', specs.respondsWith);
- it('should respond to GET "/Constraints/MaxLength/abcdef" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/LengthRange/ab" with "Constraints.LengthRange(ab)"', specs.respondsWith);
- it('should respond to GET "/Constraints/LengthRange/abc" with "Constraints.LengthRange(abc)"', specs.respondsWith);
- it('should respond to GET "/Constraints/LengthRange/a" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/LengthRange/abcd" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Min/1" with "Constraints.Min(1)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Min/0" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Max/10" with "Constraints.Max(10)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Max/11" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Range/1" with "Constraints.Range(1)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Range/10" with "Constraints.Range(10)"', specs.respondsWith);
- it('should respond to GET "/Constraints/Range/0" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Range/11" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Regex/howdy" with Constraints.Regex(howdy)', specs.respondsWith);
- it('should respond to GET "/Constraints/Regex/doody" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/RegexRange/abcd" with Constraints.RegexRange(abcd)', specs.respondsWith);
- it('should respond to GET "/Constraints/RegexRange/a" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/EnumValue/0" with Constraints.EnumValue(Red)', specs.respondsWith);
- it('should respond to GET "/Constraints/EnumValue/Red" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Enum/Red" with Constraints.Enum(Red)', specs.respondsWith);
- it('should respond to GET "/Constraints/Enum/0" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Compound/5" with Constraints.Compound(5)', specs.respondsWith);
- it('should respond to GET "/Constraints/Compound/15" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Compound/abc" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/WithOptional/red" with Constraints.WithOptional(Red)', specs.respondsWith);
- it('should respond to GET "/Constraints/WithOptional" with Constraints.WithOptional()', specs.respondsWith);
- it('should respond to GET "/Constraints/WithDefault" with Constraints.WithDefault(Red)', specs.respondsWith);
- it('should respond to GET "/Constraints/WithDefault/green" with Constraints.WithDefault(Green)', specs.respondsWith);
- it('should respond to GET "/Constraints/MultipleInSegment/2x4" with Constraints.MultipleInSegment(2, 4)', specs.respondsWith);
- it('should respond to GET "/Constraints/MultipleInSegment/2x" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/MultipleInSegment/x4" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/MultipleInSegment/axb" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Query/?x=10&y=present" with Constraints.Query(10, present)', specs.respondsWith);
- it('should respond to GET "/Constraints/Query/?x=abc&y=present" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Query/?x=&y=present" with 404', specs.respondsWith);
- it('should respond to GET "/Constraints/Query/?x=10&y=" with 404', specs.respondsWith);
- });
- describe('ConstraintsInAreaPrefixController', function() {
- it('should respond to GET "/Constraints/InAreaPrefix/123" with ConstraintsInAreaPrefix.Index(123)', specs.respondsWith);
- it('should respond to GET "/Constraints/InAReaPrefix/abc" with 404', specs.respondsWith);
- });
- describe('ConstraintsInRoutePrefixController', function() {
- it('should respond to GET "/Constraints/InRoutePrefix/123" with ConstraintsInRoutePrefix.Index(123)', specs.respondsWith);
- it('should respond to GET "/Constraints/InRoutePrefix/abc" with 404', specs.respondsWith);
- });
-});
diff --git a/src/AttributeRouting.Tests/Integration/js/specs/defaults.js b/src/AttributeRouting.Tests/Integration/js/specs/defaults.js
deleted file mode 100644
index 321be4d..0000000
--- a/src/AttributeRouting.Tests/Integration/js/specs/defaults.js
+++ /dev/null
@@ -1,21 +0,0 @@
-///
-///
-
-describe('Route Defaults', function() {
- describe('DefaultsController', function () {
- it('should respond to GET "/Defaults/Inline" with "Defaults.Inline(param, query)"', specs.respondsWith);
- it('should respond to GET "/Defaults/Optional" with "Defaults.Optional(, )"', specs.respondsWith);
- it('should respond to GET "/Defaults/Optional/param" with "Defaults.Optional(param, )"', specs.respondsWith);
- it('should respond to GET "/Defaults/Optional/?q=bleary" with "Defaults.Optional(, bleary)"', specs.respondsWith);
- it('should respond to GET "/Defaults/ControllerName" with "Defaults.ControllerName"', specs.respondsWith);
- it('should respond to GET "/Defaults/ActionName" with "Defaults.ActionName"', specs.respondsWith);
- });
- describe('HttpDefaultsController', function () {
- it('should respond to GET "/HttpDefaults/Inline" with "HttpDefaults.Inline(param, query)"', specs.respondsWith);
- it('should respond to GET "/HttpDefaults/Optional" with "HttpDefaults.Optional(, )"', specs.respondsWith);
- it('should respond to GET "/HttpDefaults/Optional/param" with "HttpDefaults.Optional(param, )"', specs.respondsWith);
- it('should respond to GET "/HttpDefaults/Optional/?q=weary" with "HttpDefaults.Optional(, weary)"', specs.respondsWith);
- it('should respond to GET "/HttpDefaults/ControllerName" with "HttpDefaults.ControllerName"', specs.respondsWith);
- it('should respond to GET "/HttpDefaults/ActionName" with "HttpDefaults.ActionName"', specs.respondsWith);
- });
-});
diff --git a/src/AttributeRouting.Tests/Integration/js/specs/matchers.js b/src/AttributeRouting.Tests/Integration/js/specs/matchers.js
deleted file mode 100644
index abf9cae..0000000
--- a/src/AttributeRouting.Tests/Integration/js/specs/matchers.js
+++ /dev/null
@@ -1,11 +0,0 @@
-beforeEach(function() {
- this.addMatchers({
- toEqualValue: function (expected) {
- var actual = this.actual;
- this.message = function() {
- return "Expected " + actual + " to equal " + expected;
- };
- return actual.toLowerCase() === expected.toLowerCase();
- }
- });
-});
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Integration/js/specs/specs.js b/src/AttributeRouting.Tests/Integration/js/specs/specs.js
deleted file mode 100644
index fe55b95..0000000
--- a/src/AttributeRouting.Tests/Integration/js/specs/specs.js
+++ /dev/null
@@ -1,57 +0,0 @@
-var specs = (function() {
-
- function fetchResponse(method, url, callback) {
- var response = undefined;
-
- $.ajax({
- type: method,
- url: url,
- success: function(res) {
- response = res;
- },
- error: function(res) {
- response = res.status + '';
- }
- });
-
- waitsFor(function() {
- return response !== undefined;
- }, 1000);
-
- runs(function() {
- callback(response);
- });
- }
-
- function parseSpecDescription(description) {
- var parser = new RegExp(/should respond to (\w+) "([^\"]+)" with "?([^\"]+|\d+)"?/i),
- match = parser.exec(description);
-
- // Validate the format of the spec's description.
- if (!match || match.length !== 4) {
- throw new Error('The spec description is not valid.\n' +
- 'Expected: should respond to METHOD "/url" with {"response" or STATUS}');
- }
-
- // Parse the relevant params from the description.
- return {
- method: match[1],
- url: match[2],
- expectedResponse: match[3]
- };
- }
-
- function respondsWith() {
- var params = parseSpecDescription(this.description);
-
- // Ensure we get the expected response.
- fetchResponse(params.method, params.url, function (response) {
- expect(response).toEqualValue(params.expectedResponse);
- });
- }
-
- // Export shared specs:
- return {
- respondsWith: respondsWith
- };
-}());
diff --git a/src/AttributeRouting.Tests/Properties/AssemblyInfo.cs b/src/AttributeRouting.Tests/Properties/AssemblyInfo.cs
deleted file mode 100644
index 7e71053..0000000
--- a/src/AttributeRouting.Tests/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("AttributeRouting.Tests")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("AttributeRouting.Tests")]
-[assembly: AssemblyCopyright("Copyright © 2013")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("b9f3a6cd-a204-4305-95a8-005982a72d13")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Revision and Build Numbers
-// by using the '*' as shown below:
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/AttributeRouting.Tests/Subjects/Color.cs b/src/AttributeRouting.Tests/Subjects/Color.cs
deleted file mode 100644
index 3723edf..0000000
--- a/src/AttributeRouting.Tests/Subjects/Color.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace AttributeRouting.Tests.Subjects
-{
- public enum Color
- {
- Red,
- Orange,
- Yellow,
- Blue,
- Green,
- Indigo,
- Violet
- }
-}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Subjects/Http/HttpBasicsController.cs b/src/AttributeRouting.Tests/Subjects/Http/HttpBasicsController.cs
deleted file mode 100644
index 9ffdfd9..0000000
--- a/src/AttributeRouting.Tests/Subjects/Http/HttpBasicsController.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System.Web.Http;
-using AttributeRouting.Helpers;
-using AttributeRouting.Web.Http;
-
-namespace AttributeRouting.Tests.Subjects.Http
-{
- [RoutePrefix("HttpBasics")]
- public class HttpBasicsController : ApiController
- {
- [GET(""), HttpGet]
- public string Index()
- {
- return "HttpBasics.Index";
- }
-
- [POST(""), HttpPost]
- public string Create()
- {
- return "HttpBasics.Create";
- }
-
- [PUT("{id}"), HttpPut]
- public string Update(string id)
- {
- return "HttpBasics.Update({0})".FormatWith(id);
- }
-
- [DELETE("{id}"), HttpDelete]
- public string Delete(string id)
- {
- return "HttpBasics.Delete({0})".FormatWith(id);
- }
- }
-}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Subjects/Http/HttpDefaultsController.cs b/src/AttributeRouting.Tests/Subjects/Http/HttpDefaultsController.cs
deleted file mode 100644
index 72322e5..0000000
--- a/src/AttributeRouting.Tests/Subjects/Http/HttpDefaultsController.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System.Web.Http;
-using AttributeRouting.Helpers;
-using AttributeRouting.Web.Http;
-
-namespace AttributeRouting.Tests.Subjects.Http
-{
- [RoutePrefix("HttpDefaults")]
- public class HttpDefaultsController : ApiController
- {
- [GET("Inline/{p=param}?{q=query}"), HttpGet]
- public string Inline(string p, string q)
- {
- return "HttpDefaults.Inline({0}, {1})".FormatWith(p, q);
- }
-
- [GET("Optional/{p?}?{q?}"), HttpGet]
- public string Optional(string p = "", string q = "")
- {
- return "HttpDefaults.Optional({0}, {1})".FormatWith(p, q);
- }
-
- [GET("{controller}/ControllerName", IsAbsoluteUrl = true), HttpGet]
- public string ControllerName()
- {
- return "HttpDefaults.ControllerName";
- }
-
- [GET("{action}"), HttpGet]
- public string ActionName()
- {
- return "HttpDefaults.ActionName";
- }
- }
-}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Subjects/Mvc/BasicsController.cs b/src/AttributeRouting.Tests/Subjects/Mvc/BasicsController.cs
deleted file mode 100644
index 535aa53..0000000
--- a/src/AttributeRouting.Tests/Subjects/Mvc/BasicsController.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System.Web.Mvc;
-using AttributeRouting.Helpers;
-using AttributeRouting.Web.Mvc;
-
-namespace AttributeRouting.Tests.Subjects.Mvc
-{
- [RoutePrefix("Basics")]
- public class BasicsController : Controller
- {
- [GET("")]
- public string Index()
- {
- return "Basics.Index";
- }
-
- [POST("")]
- public string Create()
- {
- return "Basics.Create";
- }
-
- [PUT("{id}")]
- public string Update(string id)
- {
- return "Basics.Update({0})".FormatWith(id);
- }
-
- [DELETE("{id}")]
- public string Delete(string id)
- {
- return "Basics.Delete({0})".FormatWith(id);
- }
- }
-}
diff --git a/src/AttributeRouting.Tests/Subjects/Mvc/ConstraintsControllers.cs b/src/AttributeRouting.Tests/Subjects/Mvc/ConstraintsControllers.cs
deleted file mode 100644
index a187c92..0000000
--- a/src/AttributeRouting.Tests/Subjects/Mvc/ConstraintsControllers.cs
+++ /dev/null
@@ -1,181 +0,0 @@
-using System;
-using System.Web.Mvc;
-using AttributeRouting.Helpers;
-using AttributeRouting.Web.Mvc;
-
-namespace AttributeRouting.Tests.Subjects.Mvc
-{
- [RoutePrefix("Constraints")]
- public class ConstraintsController : Controller
- {
- [GET("Alpha/{x:alpha}")]
- public string Alpha(string x)
- {
- return "Constraints.Alpha({0})".FormatWith(x);
- }
-
- [GET("Int/{x:int}")]
- public string Int(int x)
- {
- return "Constraints.Int({0})".FormatWith(x);
- }
-
- [GET("Long/{x:long}")]
- public string Long(long x)
- {
- return "Constraints.Long({0})".FormatWith(x);
- }
-
- [GET("Float/{x:float}")]
- public string Float(float x)
- {
- return "Constraints.Float({0})".FormatWith(x);
- }
-
- [GET("Double/{x:double}")]
- public string Double(double x)
- {
- return "Constraints.Double({0})".FormatWith(x);
- }
-
- [GET("Decimal/{x:decimal}")]
- public string Decimal(decimal x)
- {
- return "Constraints.Decimal({0})".FormatWith(x);
- }
-
- [GET("Bool/{x:bool}")]
- public string Bool(bool x)
- {
- return "Constraints.Bool({0})".FormatWith(x);
- }
-
- [GET("Guid/{x:guid}")]
- public string Guid(Guid x)
- {
- return "Constraints.Guid({0})".FormatWith(x);
- }
-
- [GET("DateTime/{x:datetime}")]
- public string DateTime(DateTime x)
- {
- return "Constraints.DateTime({0})".FormatWith(x);
- }
-
- [GET("Length/{x:length(1)}")]
- public string Length(string x)
- {
- return "Constraints.Length({0})".FormatWith(x);
- }
-
- [GET("MinLength/{x:minlength(5)}")]
- public string MinLength(string x)
- {
- return "Constraints.MinLength({0})".FormatWith(x);
- }
-
- [GET("MaxLength/{x:maxlength(5)}")]
- public string MaxLength(string x)
- {
- return "Constraints.MaxLength({0})".FormatWith(x);
- }
-
- [GET("LengthRange/{x:length(2, 3)}")]
- public string LengthRange(string x)
- {
- return "Constraints.LengthRange({0})".FormatWith(x);
- }
-
- [GET("Min/{x:min(1)}")]
- public string Min(int x)
- {
- return "Constraints.Min({0})".FormatWith(x);
- }
-
- [GET("Max/{x:max(10)}")]
- public string Max(int x)
- {
- return "Constraints.Max({0})".FormatWith(x);
- }
-
- [GET("Range/{x:range(1, 10)}")]
- public string Range(int x)
- {
- return "Constraints.Range({0})".FormatWith(x);
- }
-
- [GET(@"Regex/{x:regex(^howdy$)}")]
- public string Regex(string x)
- {
- return "Constraints.Regex({0})".FormatWith(x);
- }
-
- [GET(@"RegexRange/{x:regex(\w{2,5})}")]
- public string RegexRange(string x)
- {
- return "Constraints.RegexRange({0})".FormatWith(x);
- }
-
- [GET("EnumValue/{x:colorValue}")]
- public string EnumValue(Color x)
- {
- return "Constraints.EnumValue({0})".FormatWith(x);
- }
-
- [GET("Enum/{x:color}")]
- public string Enum(Color x)
- {
- return "Constraints.Enum({0})".FormatWith(x);
- }
-
- [GET("Compound/{x:int:max(10)}")]
- public string Compound(int x)
- {
- return "Constraints.Compound({0})".FormatWith(x);
- }
-
- [GET("WithOptional/{x:color?}")]
- public string WithOptional(Color? x)
- {
- return "Constraints.WithOptional({0})".FormatWith(x);
- }
-
- [GET("WithDefault/{x:color=red}")]
- public string WithDefault(Color x)
- {
- return "Constraints.WithDefault({0})".FormatWith(x);
- }
-
- [GET("MultipleInSegment/{x:int}x{y:int}")]
- public string MultipleInSegment(int x, int y)
- {
- return "Constraints.MultipleInSegment({0}, {1})".FormatWith(x, y);
- }
-
- [GET("Query?{x:int}&{y}")]
- public string Query(int x, string y)
- {
- return "Constraints.Query({0}, {1})".FormatWith(x, y);
- }
- }
-
- [RouteArea("Constraints", AreaUrl = "Constraints/InAreaPrefix/{x:int}")]
- public class ConstraintsInAreaPrefixController : Controller
- {
- [GET("")]
- public string Index(int x)
- {
- return "ConstraintsInAreaPrefix.Index({0})".FormatWith(x);
- }
- }
-
- [RoutePrefix("Constraints/InRoutePrefix/{x:int}")]
- public class ConstraintsInRoutePrefixController : Controller
- {
- [GET("")]
- public string Index(int x)
- {
- return "ConstraintsInRoutePrefix.Index({0})".FormatWith(x);
- }
- }
-}
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Subjects/Mvc/DefaultsController.cs b/src/AttributeRouting.Tests/Subjects/Mvc/DefaultsController.cs
deleted file mode 100644
index 95b6f88..0000000
--- a/src/AttributeRouting.Tests/Subjects/Mvc/DefaultsController.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System.Web.Mvc;
-using AttributeRouting.Helpers;
-using AttributeRouting.Web.Mvc;
-
-namespace AttributeRouting.Tests.Subjects.Mvc
-{
- [RoutePrefix("Defaults")]
- public class DefaultsController : Controller
- {
- [GET("Inline/{p=param}?{q=query}")]
- public string Inline(string p, string q)
- {
- return "Defaults.Inline({0}, {1})".FormatWith(p, q);
- }
-
- [GET("Optional/{p?}?{q?}")]
- public string Optional(string p, string q)
- {
- return "Defaults.Optional({0}, {1})".FormatWith(p, q);
- }
-
- [GET("{controller}/ControllerName", IsAbsoluteUrl = true)]
- public string ControllerName()
- {
- return "Defaults.ControllerName";
- }
-
- [GET("{action}")]
- public string ActionName()
- {
- return "Defaults.ActionName";
- }
- }
-}
diff --git a/src/AttributeRouting.Tests/Web.Debug.config b/src/AttributeRouting.Tests/Web.Debug.config
deleted file mode 100644
index a83c1d4..0000000
--- a/src/AttributeRouting.Tests/Web.Debug.config
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Web.Release.config b/src/AttributeRouting.Tests/Web.Release.config
deleted file mode 100644
index 658b48e..0000000
--- a/src/AttributeRouting.Tests/Web.Release.config
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/Web.config b/src/AttributeRouting.Tests/Web.config
deleted file mode 100644
index d47eb9f..0000000
--- a/src/AttributeRouting.Tests/Web.config
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/AttributeRouting.Tests/packages.config b/src/AttributeRouting.Tests/packages.config
deleted file mode 100644
index bb2b4dc..0000000
--- a/src/AttributeRouting.Tests/packages.config
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
From f3c16fe9e40310b702ae2c390e9847ad2fa71686 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Mon, 11 Mar 2013 12:43:14 -0600
Subject: [PATCH 82/97] removed project ref from sln
---
src/AttributeRouting.sln | 13 -------------
src/SharedAssemblyInfo.cs | 6 +++---
2 files changed, 3 insertions(+), 16 deletions(-)
diff --git a/src/AttributeRouting.sln b/src/AttributeRouting.sln
index 96fba6a..99fb999 100644
--- a/src/AttributeRouting.sln
+++ b/src/AttributeRouting.sln
@@ -33,8 +33,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Tests.Self
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Web.Http", "AttributeRouting.Web.Http\AttributeRouting.Web.Http.csproj", "{CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Tests", "AttributeRouting.Tests\AttributeRouting.Tests.csproj", "{2A6712B2-0912-457D-A66E-982640E9F411}"
-EndProject
Global
GlobalSection(SubversionScc) = preSolution
Svn-Managed = True
@@ -139,16 +137,6 @@ Global
{CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Release|x86.ActiveCfg = Release|Any CPU
- {2A6712B2-0912-457D-A66E-982640E9F411}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2A6712B2-0912-457D-A66E-982640E9F411}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2A6712B2-0912-457D-A66E-982640E9F411}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {2A6712B2-0912-457D-A66E-982640E9F411}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {2A6712B2-0912-457D-A66E-982640E9F411}.Debug|x86.ActiveCfg = Debug|Any CPU
- {2A6712B2-0912-457D-A66E-982640E9F411}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2A6712B2-0912-457D-A66E-982640E9F411}.Release|Any CPU.Build.0 = Release|Any CPU
- {2A6712B2-0912-457D-A66E-982640E9F411}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {2A6712B2-0912-457D-A66E-982640E9F411}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {2A6712B2-0912-457D-A66E-982640E9F411}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -157,6 +145,5 @@ Global
{E832A82B-2302-4113-83E3-261446E22C87} = {840281A8-8870-417F-9B24-ED0E866608A2}
{486087C6-E95B-4FE2-8263-7B6B2DF81888} = {840281A8-8870-417F-9B24-ED0E866608A2}
{0D2A13E6-A092-402E-B8FE-035F3A620B86} = {840281A8-8870-417F-9B24-ED0E866608A2}
- {2A6712B2-0912-457D-A66E-982640E9F411} = {840281A8-8870-417F-9B24-ED0E866608A2}
EndGlobalSection
EndGlobal
diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs
index f506102..045acbe 100644
--- a/src/SharedAssemblyInfo.cs
+++ b/src/SharedAssemblyInfo.cs
@@ -7,7 +7,7 @@
[assembly: AssemblyProduct("AttributeRouting")]
[assembly: AssemblyCopyright("Copyright 2010-2013 Tim McCall")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("3.5.3")]
-[assembly: AssemblyFileVersion("3.5.3")]
-[assembly: AssemblyInformationalVersion("3.5.3")]
+[assembly: AssemblyVersion("3.5.4")]
+[assembly: AssemblyFileVersion("3.5.4")]
+[assembly: AssemblyInformationalVersion("3.5.4")]
[assembly: AssemblyConfiguration("Release")]
From 697c1c6c64dc61e94ce61a70321d0ee7bc5d6a15 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Mon, 11 Mar 2013 12:45:05 -0600
Subject: [PATCH 83/97] updated readme for 3.5.4
---
README.textile | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/README.textile b/README.textile
index ec79586..4edb673 100644
--- a/README.textile
+++ b/README.textile
@@ -4,6 +4,11 @@ h2. Please refer to "attributerouting.net":http://attributerouting.net/ for docu
h3. Changelog
+_3.5.4_
+
+* #217 - Improved perf of ObjectExtensions.SafeGet. Thanks youssefm!
+* BUG FIX: fixed bug in handling of default values with querystring params.
+
_3.5.3_
* #216 - testing optional constraint for mvc against null/empty in addition to UrlParameter.Optional.
From b026938b2d6e41b972f2c9a9a02af2d8240933b4 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 12 Mar 2013 11:26:42 -0600
Subject: [PATCH 84/97] #218 - fixed handling of querystring default values.
---
.../Subjects/BugFixesControllers.cs | 24 +++++++-
.../Tests/BugFixTests.cs | 24 +++++++-
.../Framework/HttpAttributeRoute.cs | 6 ++
.../Framework/AttributeRoute.cs | 6 ++
.../Framework/AttributeRouteVisitor.cs | 16 ++++++
.../Framework/IAttributeRoute.cs | 5 ++
.../Framework/RouteBuilder.cs | 56 +++++++++++++++----
.../Helpers/ObjectExtensions.cs | 5 +-
8 files changed, 125 insertions(+), 17 deletions(-)
diff --git a/src/AttributeRouting.Specs/Subjects/BugFixesControllers.cs b/src/AttributeRouting.Specs/Subjects/BugFixesControllers.cs
index 4e6940e..8ce4ed2 100644
--- a/src/AttributeRouting.Specs/Subjects/BugFixesControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/BugFixesControllers.cs
@@ -1,10 +1,32 @@
using System;
using System.Web.Http;
using System.Web.Mvc;
-using AttributeRouting.Web.Mvc;
+using AttributeRouting.Web.Mvc;
namespace AttributeRouting.Specs.Subjects
{
+ [RoutePrefix("Issue-218")]
+ public class Issue218TestController : Controller
+ {
+ [GET("Optional-Query?{categoryId:long?}")]
+ public string OptionalQuery(long categoryId = 0)
+ {
+ return "Category id = " + categoryId;
+ }
+
+ [GET("Default-Query?{categoryId:long=123}")]
+ public string DefaultQuery(long categoryId)
+ {
+ return "Category id = " + categoryId;
+ }
+
+ [GET("No-Query")]
+ public string NoQuery(long categoryId = 0)
+ {
+ return "Category id = " + categoryId;
+ }
+ }
+
[RoutePrefix("BugFixes")]
public class BugFixesController : Controller
{
diff --git a/src/AttributeRouting.Specs/Tests/BugFixTests.cs b/src/AttributeRouting.Specs/Tests/BugFixTests.cs
index e1579e1..7372780 100644
--- a/src/AttributeRouting.Specs/Tests/BugFixTests.cs
+++ b/src/AttributeRouting.Specs/Tests/BugFixTests.cs
@@ -18,7 +18,29 @@
namespace AttributeRouting.Specs.Tests
{
public class BugFixTests
- {
+ {
+ [Test]
+ public void Issue218_Url_generation_with_optional_query_params()
+ {
+ // re: issue #218
+
+ var routes = RouteTable.Routes;
+ routes.Clear();
+ routes.MapAttributeRoutes(config => config.AddRoutesFromController());
+ RouteTable.Routes.Cast().LogTo(Console.Out);
+
+ var urlHelper = new UrlHelper(MockBuilder.BuildRequestContext());
+
+ Assert.That(urlHelper.Action("NoQuery", "Issue218Test", new { categoryId = 12 }),
+ Is.EqualTo("/Issue-218/No-Query?categoryId=12"));
+
+ Assert.That(urlHelper.Action("OptionalQuery", "Issue218Test", new { categoryId = 12 }),
+ Is.EqualTo("/Issue-218/Optional-Query?categoryId=12"));
+
+ Assert.That(urlHelper.Action("DefaultQuery", "Issue218Test"),
+ Is.EqualTo("/Issue-218/Default-Query?categoryId=123"));
+ }
+
[Test]
public void Issue161_Querystring_param_constraints_mucks_up_url_generation()
{
diff --git a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
index b56ec0a..5fe6203 100644
--- a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
+++ b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
@@ -33,6 +33,7 @@ public HttpAttributeRoute(string url,
_configuration = configuration;
_visitor = new AttributeRouteVisitor(this, configuration);
QueryStringConstraints = new RouteValueDictionary();
+ QueryStringDefaults = new RouteValueDictionary();
}
public bool? AppendTrailingSlash { get; set; }
@@ -62,6 +63,8 @@ IDictionary IAttributeRoute.Defaults
public bool? PreserveCaseForUrlParameters { get; set; }
public IDictionary QueryStringConstraints { get; set; }
+
+ public IDictionary QueryStringDefaults { get; set; }
public string RouteName { get; set; }
@@ -121,6 +124,9 @@ public override IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestM
public override IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary values)
{
+ // Add querystring default values if applicable.
+ _visitor.AddQueryStringDefaultsToRouteValues(values);
+
// Let the underlying route do its thing.
var virtualPathData = base.GetVirtualPath(request, values);
if (virtualPathData == null)
diff --git a/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs b/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
index a0e0406..ae5d950 100644
--- a/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
+++ b/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
@@ -32,6 +32,7 @@ public AttributeRoute(string url,
_configuration = configuration;
_visitor = new AttributeRouteVisitor(this, configuration);
QueryStringConstraints = new RouteValueDictionary();
+ QueryStringDefaults = new RouteValueDictionary();
}
public bool? AppendTrailingSlash { get; set; }
@@ -62,6 +63,8 @@ IDictionary IAttributeRoute.Defaults
public IDictionary QueryStringConstraints { get; set; }
+ public IDictionary QueryStringDefaults { get; set; }
+
public string RouteName { get; set; }
public string Subdomain { get; set; }
@@ -113,6 +116,9 @@ public override RouteData GetRouteData(HttpContextBase httpContext)
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
+ // Add querystring default values if applicable.
+ _visitor.AddQueryStringDefaultsToRouteValues(values);
+
// Let the underlying route do its thing.
var virtualPathData = base.GetVirtualPath(requestContext, values);
if (virtualPathData == null)
diff --git a/src/AttributeRouting/Framework/AttributeRouteVisitor.cs b/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
index cfb4f36..f0ff905 100644
--- a/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
+++ b/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
@@ -51,6 +52,21 @@ private string StaticLeftPartOfUrl
}
}
+ ///
+ /// Adds querystring default values to route values collection if they aren't already present.
+ ///
+ /// The route values.
+ public void AddQueryStringDefaultsToRouteValues(IDictionary routeValues)
+ {
+ foreach (var queryStringDefault in _route.QueryStringDefaults)
+ {
+ if (!routeValues.ContainsKey(queryStringDefault.Key))
+ {
+ routeValues.Add(queryStringDefault.Key, queryStringDefault.Value);
+ }
+ }
+ }
+
///
/// Performs lowercasing and appends trailing slash if this route is so configured.
///
diff --git a/src/AttributeRouting/Framework/IAttributeRoute.cs b/src/AttributeRouting/Framework/IAttributeRoute.cs
index eac3f4f..68d17b7 100644
--- a/src/AttributeRouting/Framework/IAttributeRoute.cs
+++ b/src/AttributeRouting/Framework/IAttributeRoute.cs
@@ -50,6 +50,11 @@ public interface IAttributeRoute
///
IDictionary QueryStringConstraints { get; set; }
+ ///
+ /// Defaults dictionary for querystring defaults.
+ ///
+ IDictionary QueryStringDefaults { get; set; }
+
///
/// The name of this route, for supporting named routes.
///
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 31dbe2b..279069e 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -83,16 +83,21 @@ private string BuildTokenizedUrl(string routeUrl, string routePrefixUrl, string
}
private IEnumerable BuildRoutes(RouteSpecification routeSpec)
- {
- var defaults = CreateRouteDefaults(routeSpec);
+ {
+ // Get info needed to construct IAttributeRoute via factory method.
+ IDictionary defaults;
+ IDictionary queryStringDefaults;
+ CreateRouteDefaults(routeSpec, out defaults, out queryStringDefaults);
IDictionary constraints;
IDictionary queryStringConstraints;
CreateRouteConstraints(routeSpec, out constraints, out queryStringConstraints);
var dataTokens = CreateRouteDataTokens(routeSpec);
var url = CreateRouteUrl(defaults, routeSpec);
+ // Build the route.
var routes = _routeFactory.CreateAttributeRoutes(url, defaults, constraints, dataTokens);
+ // Extend the factory generated route:
foreach (var route in routes)
{
var routeName = CreateRouteName(routeSpec);
@@ -103,6 +108,7 @@ private IEnumerable BuildRoutes(RouteSpecification routeSpec)
}
route.QueryStringConstraints = queryStringConstraints;
+ route.QueryStringDefaults = queryStringDefaults;
route.Translations = CreateRouteTranslations(routeSpec);
route.Subdomain = routeSpec.Subdomain;
route.UseLowercaseRoute = routeSpec.UseLowercaseRoute;
@@ -291,28 +297,40 @@ private IDictionary CreateRouteDataTokens(RouteSpecification rou
return dataTokens;
}
- private IDictionary CreateRouteDefaults(RouteSpecification routeSpec)
- {
- var defaults = new Dictionary
+ private IDictionary CreateRouteDefaults(RouteSpecification routeSpec, out IDictionary defaults, out IDictionary queryStringDefaults)
+ {
+ // Going to return individual collections for:
+ // - path routes defaults (which will go into the generated route's Defaults prop),
+ // - and query string route defaults (which will not work perfectly with the MS bits, and need special treatment by IAttributeRoute impls).
+ defaults = new Dictionary
{
{ "controller", routeSpec.ControllerName },
{ "action", routeSpec.ActionName }
};
+ queryStringDefaults = new Dictionary();
- // Work from a complete, tokenized url; ie: support defaults in area urls, route prefix urls, and route urls.
+ // Work from a complete, tokenized url; ie: support constraints in area urls, route prefix urls, and route urls.
var tokenizedUrl = BuildTokenizedUrl(routeSpec.RouteUrl, routeSpec.RoutePrefixUrl, routeSpec.AreaUrl, routeSpec);
var urlParameters = GetUrlParameterContents(tokenizedUrl).ToList();
+ // Need to keep track of which are path and query params.
+ var pathOnlyUrl = RemoveQueryString(tokenizedUrl);
+ var pathOnlyUrlParameters = GetUrlParameterContents(pathOnlyUrl).ToList();
+ var queryStringParameters = urlParameters.Except(pathOnlyUrlParameters).ToList();
+
// Inspect the url path for optional parameters, specified with a trailing ?
- foreach (var parameter in urlParameters.Where(p => p.EndsWith("?")))
+ foreach (var parameter in pathOnlyUrlParameters.Where(p => p.EndsWith("?")))
{
+ // Strip off any optional tokens.
var parameterName = parameter.TrimEnd('?');
+ // Strip off any inline constraints.
if (parameterName.Contains(':'))
{
parameterName = parameterName.Substring(0, parameterName.IndexOf(':'));
}
+ // Do not override default defaults.
if (defaults.ContainsKey(parameterName))
{
continue;
@@ -325,20 +343,31 @@ private IDictionary CreateRouteDefaults(RouteSpecification route
foreach (var parameter in urlParameters.Where(p => p.Contains('=')))
{
var indexOfEquals = parameter.IndexOf('=');
- var parameterName = parameter.Substring(0, indexOfEquals);
+ var parameterName = parameter.Substring(0, indexOfEquals);
+ var parameterIsInQueryString = queryStringParameters.Contains(parameter);
+ // Strip off inline constraints
if (parameterName.Contains(':'))
{
parameterName = parameterName.Substring(0, parameterName.IndexOf(':'));
}
+ // Do not override default defaults.
if (defaults.ContainsKey(parameterName))
{
continue;
}
var defaultValue = parameter.Substring(indexOfEquals + 1, parameter.Length - indexOfEquals - 1);
- defaults.Add(parameterName, defaultValue);
+
+ if (parameterIsInQueryString)
+ {
+ queryStringDefaults.Add(parameterName, defaultValue);
+ }
+ else
+ {
+ defaults.Add(parameterName, defaultValue);
+ }
}
return defaults;
@@ -387,9 +416,11 @@ private IEnumerable CreateRouteTranslations(RouteSpecification
//*********************************************
// Otherwise, build a translated route
- // REVIEW: Could probably forgo processing defaults, constraints, and data tokens for translated routes.
-
- var defaults = CreateRouteDefaults(routeSpec);
+ // REVIEW: Could probably forgo processing defaults, constraints, and data tokens for translated routes.
+
+ IDictionary defaults;
+ IDictionary queryStringDefaults;
+ CreateRouteDefaults(routeSpec, out defaults, out queryStringDefaults);
IDictionary constraints;
IDictionary queryStringConstraints;
CreateRouteConstraints(routeSpec, out constraints, out queryStringConstraints);
@@ -412,6 +443,7 @@ private IEnumerable CreateRouteTranslations(RouteSpecification
}
translatedRoute.QueryStringConstraints = queryStringConstraints;
+ translatedRoute.QueryStringDefaults = queryStringDefaults;
translatedRoute.CultureName = cultureName;
translatedRoute.DataTokens.Add("cultureName", cultureName);
diff --git a/src/AttributeRouting/Helpers/ObjectExtensions.cs b/src/AttributeRouting/Helpers/ObjectExtensions.cs
index f068e83..6015b09 100644
--- a/src/AttributeRouting/Helpers/ObjectExtensions.cs
+++ b/src/AttributeRouting/Helpers/ObjectExtensions.cs
@@ -1,5 +1,4 @@
using System;
-using System.Linq.Expressions;
namespace AttributeRouting.Helpers
{
@@ -14,7 +13,7 @@ public static bool HasNoValue(this object obj)
}
///
- /// Will walk the given expression tree to get the value at the leaf.
+ /// Will attempt to get the value returned from the delegate.
/// If a NullReferenceException is thrown, the default for the leaf type will be returned.
///
public static TResult SafeGet(this T obj, Func memberExpression)
@@ -23,7 +22,7 @@ public static TResult SafeGet(this T obj, Func memberExp
}
///
- /// Will walk the given expression tree to get the value at the leaf.
+ /// Will attempt to get the value returned from the delegate.
/// If a NullReferenceException is thrown, the given default will be returned.
///
public static TResult SafeGet(this T obj, Func memberExpression, TResult defaultValue)
From 506e09e9fcbcae0b394db49179d2a71be842b460 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 12 Mar 2013 11:28:38 -0600
Subject: [PATCH 85/97] updated readme and assembly info for 3.5.5
---
README.textile | 4 ++++
src/SharedAssemblyInfo.cs | 6 +++---
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/README.textile b/README.textile
index 4edb673..d8da277 100644
--- a/README.textile
+++ b/README.textile
@@ -4,6 +4,10 @@ h2. Please refer to "attributerouting.net":http://attributerouting.net/ for docu
h3. Changelog
+_3.5.5_
+
+* #218 - BUG FIX: fixed handling of querystring default values.
+
_3.5.4_
* #217 - Improved perf of ObjectExtensions.SafeGet. Thanks youssefm!
diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs
index 045acbe..10df449 100644
--- a/src/SharedAssemblyInfo.cs
+++ b/src/SharedAssemblyInfo.cs
@@ -7,7 +7,7 @@
[assembly: AssemblyProduct("AttributeRouting")]
[assembly: AssemblyCopyright("Copyright 2010-2013 Tim McCall")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("3.5.4")]
-[assembly: AssemblyFileVersion("3.5.4")]
-[assembly: AssemblyInformationalVersion("3.5.4")]
+[assembly: AssemblyVersion("3.5.5")]
+[assembly: AssemblyFileVersion("3.5.5")]
+[assembly: AssemblyInformationalVersion("3.5.5")]
[assembly: AssemblyConfiguration("Release")]
From b5c2251e088f4fbc0d62b1c01b81077f0cfe0227 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 12 Mar 2013 12:45:49 -0600
Subject: [PATCH 86/97] allowing default constraints to apply to query params.
---
.../Framework/AttributeReflector.cs | 2 +-
.../Framework/AttributeRouteVisitor.cs | 24 +-----
.../Framework/IAttributeRoute.cs | 3 +-
.../Framework/IAttributeRouteFactory.cs | 3 +-
.../Framework/IParameterFactory.cs | 3 +-
.../Framework/IRouteConstraintFactory.cs | 3 +-
.../Framework/RouteBuilder.cs | 74 +++++++++++--------
.../Helpers/StringExtensions.cs | 68 +++++++++++------
8 files changed, 95 insertions(+), 85 deletions(-)
diff --git a/src/AttributeRouting/Framework/AttributeReflector.cs b/src/AttributeRouting/Framework/AttributeReflector.cs
index 069a979..7b18ffb 100644
--- a/src/AttributeRouting/Framework/AttributeReflector.cs
+++ b/src/AttributeRouting/Framework/AttributeReflector.cs
@@ -148,7 +148,7 @@ private static string GetAreaUrl(RouteAreaAttribute routeAreaAttribute, string s
}
// If we're given an area url or an area name, then use it.
- // Otherwise, use the last section of the namespace of the controller, as a convention.
+ // Otherwise, get the area name from the namespace of the controller, as a convention.
var areaUrlOrName = routeAreaAttribute.AreaUrl ?? routeAreaAttribute.AreaName;
return areaUrlOrName ?? controllerType.GetConventionalAreaName();
}
diff --git a/src/AttributeRouting/Framework/AttributeRouteVisitor.cs b/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
index f0ff905..e822cc3 100644
--- a/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
+++ b/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
@@ -254,7 +254,7 @@ public bool IsSubdomainMatched(string requestedSubdomain)
///
///
/// Delegate used to process the query constraints according to the underlying route framework.
- /// Accepts a constraint and parameter name and returns tru if the constraint passes.
+ /// Accepts a constraint and parameter name and returns true if the constraint passes.
///
/// True if all query string constraints pass or if there are none to test.
///
@@ -283,7 +283,7 @@ public bool ProcessQueryStringConstraints(Func processCons
private static string AppendTrailingSlashToVirtualPath(string virtualPath)
{
string path, query;
- GetPathAndQuery(virtualPath, out path, out query);
+ virtualPath.GetPathAndQuery(out path, out query);
if (path.HasValue() && !path.EndsWith("/"))
{
@@ -293,28 +293,10 @@ private static string AppendTrailingSlashToVirtualPath(string virtualPath)
return path + query;
}
- private static void GetPathAndQuery(string virtualPath, out string path, out string query)
- {
- // NOTE: Do not lowercase the querystring vals
- var match = PathAndQueryRegex.Match(virtualPath);
-
- // Just covering my backside here in case the regex fails for some reason.
- if (!match.Success)
- {
- path = virtualPath;
- query = null;
- }
- else
- {
- path = match.Groups["path"].Value;
- query = match.Groups["query"].Value;
- }
- }
-
private static string TransformVirtualPathToLowercase(string virtualPath)
{
string path, query;
- GetPathAndQuery(virtualPath, out path, out query);
+ virtualPath.GetPathAndQuery(out path, out query);
return path.ToLowerInvariant() + query;
}
diff --git a/src/AttributeRouting/Framework/IAttributeRoute.cs b/src/AttributeRouting/Framework/IAttributeRoute.cs
index 68d17b7..cc94ff2 100644
--- a/src/AttributeRouting/Framework/IAttributeRoute.cs
+++ b/src/AttributeRouting/Framework/IAttributeRoute.cs
@@ -3,8 +3,7 @@
namespace AttributeRouting.Framework
{
///
- /// Abstraction used by
- /// to generate routes with custom functionality.
+ /// Abstraction used by to generate routes with custom functionality.
///
///
/// Due to different route implementations in
diff --git a/src/AttributeRouting/Framework/IAttributeRouteFactory.cs b/src/AttributeRouting/Framework/IAttributeRouteFactory.cs
index 0351efb..3b1258a 100644
--- a/src/AttributeRouting/Framework/IAttributeRouteFactory.cs
+++ b/src/AttributeRouting/Framework/IAttributeRouteFactory.cs
@@ -3,8 +3,7 @@
namespace AttributeRouting.Framework
{
///
- /// Abstraction used by
- /// to put custom routes in the underlying route table.
+ /// Abstraction used by when generating an .
///
///
/// Due to different route implementations in
diff --git a/src/AttributeRouting/Framework/IParameterFactory.cs b/src/AttributeRouting/Framework/IParameterFactory.cs
index 848f3cf..d79911f 100644
--- a/src/AttributeRouting/Framework/IParameterFactory.cs
+++ b/src/AttributeRouting/Framework/IParameterFactory.cs
@@ -1,8 +1,7 @@
namespace AttributeRouting.Framework
{
///
- /// Abstraction used by
- /// when generating optional url parameters.
+ /// Abstraction used by when generating a url parameter.
///
///
/// Due to
diff --git a/src/AttributeRouting/Framework/IRouteConstraintFactory.cs b/src/AttributeRouting/Framework/IRouteConstraintFactory.cs
index 00e3494..da63197 100644
--- a/src/AttributeRouting/Framework/IRouteConstraintFactory.cs
+++ b/src/AttributeRouting/Framework/IRouteConstraintFactory.cs
@@ -4,8 +4,7 @@
namespace AttributeRouting.Framework
{
///
- /// Abstraction used by
- /// to generate route constraints for routes.
+ /// Abstraction used by when generating a route constraint.
///
///
/// Due to
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index 279069e..e345e1a 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -36,8 +36,8 @@ public RouteBuilder(ConfigurationBase configuration)
_routeFactory = configuration.AttributeRouteFactory;
_routeConstraintFactory = configuration.RouteConstraintFactory;
_parameterFactory = configuration.ParameterFactory;
- }
-
+ }
+
///
/// Yields all the routes to register in the route table.
///
@@ -53,8 +53,26 @@ where s.Subdomain.HasValue()
select s.Subdomain).Distinct().ToList();
return routeSpecs.SelectMany(BuildRoutes);
- }
-
+ }
+
+ private void ApplyDefaultRouteConstraint(IEnumerable urlParameterNames, KeyValuePair constraint, IDictionary targetConstraints, bool inQueryString)
+ {
+ foreach (var name in urlParameterNames)
+ {
+ if (!Regex.IsMatch(name, constraint.Key) || targetConstraints.ContainsKey(name))
+ {
+ continue;
+ }
+
+ var finalConstraint = constraint.Value;
+ if (inQueryString)
+ {
+ finalConstraint = _routeConstraintFactory.CreateQueryStringRouteConstraint(finalConstraint);
+ }
+ targetConstraints.Add(name, finalConstraint);
+ }
+ }
+
private string BuildTokenizedUrl(string routeUrl, string routePrefixUrl, string areaUrl, RouteSpecification routeSpec)
{
var delimitedUrl = routeUrl + "/";
@@ -80,8 +98,8 @@ private string BuildTokenizedUrl(string routeUrl, string routePrefixUrl, string
}
return delimitedUrl.Trim('/');
- }
-
+ }
+
private IEnumerable BuildRoutes(RouteSpecification routeSpec)
{
// Get info needed to construct IAttributeRoute via factory method.
@@ -130,8 +148,8 @@ private IEnumerable BuildRoutes(RouteSpecification routeSpec)
}
}
}
- }
-
+ }
+
private void CreateRouteConstraints(RouteSpecification routeSpec, out IDictionary constraints, out IDictionary queryStringConstraints)
{
// Going to return individual collections for:
@@ -255,25 +273,19 @@ private void CreateRouteConstraints(RouteSpecification routeSpec, out IDictionar
}
} // ... go to next parameter
- // Globally configured constraints
- var detokenizedUrl = DetokenizeUrl(tokenizedUrl);
- var urlParameterNames = GetUrlParameterContents(detokenizedUrl).ToList();
+ // Globally configured constraints:
+ var detokenizedUrl = DetokenizeUrl(tokenizedUrl);
+ string path, query;
+ detokenizedUrl.GetPathAndQuery(out path, out query);
+ var urlPathParameterNames = GetUrlParameterContents(path).ToArray();
+ var urlQueryParameterNames = GetUrlParameterContents(query).ToArray();
foreach (var defaultConstraint in _configuration.DefaultRouteConstraints)
{
- var pattern = defaultConstraint.Key;
-
- foreach (var urlParameterName in urlParameterNames.Where(n => Regex.IsMatch(n, pattern)))
- {
- if (constraints.ContainsKey(urlParameterName))
- {
- continue;
- }
-
- constraints.Add(urlParameterName, defaultConstraint.Value);
- }
+ ApplyDefaultRouteConstraint(urlPathParameterNames, defaultConstraint, constraints, false);
+ ApplyDefaultRouteConstraint(urlQueryParameterNames, defaultConstraint, queryStringConstraints, true);
}
- }
-
+ }
+
private IDictionary CreateRouteDataTokens(RouteSpecification routeSpec)
{
var dataTokens = new Dictionary
@@ -295,9 +307,9 @@ private IDictionary CreateRouteDataTokens(RouteSpecification rou
}
return dataTokens;
- }
-
- private IDictionary CreateRouteDefaults(RouteSpecification routeSpec, out IDictionary defaults, out IDictionary queryStringDefaults)
+ }
+
+ private void CreateRouteDefaults(RouteSpecification routeSpec, out IDictionary defaults, out IDictionary queryStringDefaults)
{
// Going to return individual collections for:
// - path routes defaults (which will go into the generated route's Defaults prop),
@@ -369,8 +381,6 @@ private IDictionary CreateRouteDefaults(RouteSpecification route
defaults.Add(parameterName, defaultValue);
}
}
-
- return defaults;
}
private string CreateRouteName(RouteSpecification routeSpec)
@@ -520,8 +530,10 @@ private static string DetokenizeUrl(string url)
private static IEnumerable GetUrlParameterContents(string url)
{
- if (!url.HasValue())
- yield break;
+ if (!url.HasValue())
+ {
+ yield break;
+ }
var urlSegments = url.SplitAndTrim(new[] { "/" });
foreach (var urlSegment in urlSegments)
diff --git a/src/AttributeRouting/Helpers/StringExtensions.cs b/src/AttributeRouting/Helpers/StringExtensions.cs
index e5ac605..ad2b5fd 100644
--- a/src/AttributeRouting/Helpers/StringExtensions.cs
+++ b/src/AttributeRouting/Helpers/StringExtensions.cs
@@ -7,12 +7,29 @@ namespace AttributeRouting.Helpers
{
public static class StringExtensions
{
- public static bool ValueEquals(this string s, string other)
+ private static readonly Regex PathAndQueryRegex = new Regex(@"(?[^\?]*)(?\?.*)?");
+
+ public static string FormatWith(this string s, params object[] args)
{
- if (s == null)
- return other == null;
+ return String.Format(s, args);
+ }
- return s.Equals(other, StringComparison.OrdinalIgnoreCase);
+ public static void GetPathAndQuery(this string url, out string path, out string query)
+ {
+ // NOTE: Do not lowercase the querystring vals
+ var match = PathAndQueryRegex.Match(url);
+
+ // Just covering my backside here in case the regex fails for some reason.
+ if (!match.Success)
+ {
+ path = url;
+ query = null;
+ }
+ else
+ {
+ path = match.Groups["path"].Value;
+ query = match.Groups["query"].Value;
+ }
}
public static bool HasValue(this string s)
@@ -25,17 +42,22 @@ public static bool HasNoValue(this string s)
return String.IsNullOrWhiteSpace(s);
}
- public static string ValueOr(this string s, string otherValue)
+ public static bool IsValidUrl(this string s, bool allowTokens = false)
{
- if (s.HasValue())
- return s;
+ var urlParts = s.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
- return otherValue;
- }
+ var invalidUrlPatterns = new List
+ {
+ @"[#%&:<>/{0}]".FormatWith(allowTokens ? null : @"\\\+\{\}?\*"),
+ @"\.\.",
+ @"\.$",
+ @"^ ",
+ @" $"
+ };
- public static string FormatWith(this string s, params object[] args)
- {
- return String.Format(s, args);
+ var invalidUrlPattern = String.Join("|", invalidUrlPatterns);
+
+ return !urlParts.Any(p => Regex.IsMatch(p, invalidUrlPattern));
}
public static string[] SplitAndTrim(this string s, params string[] separator)
@@ -46,22 +68,20 @@ public static string[] SplitAndTrim(this string s, params string[] separator)
return s.Split(separator, StringSplitOptions.RemoveEmptyEntries).Select(i => i.Trim()).ToArray();
}
- public static bool IsValidUrl(this string s, bool allowTokens = false)
+ public static bool ValueEquals(this string s, string other)
{
- var urlParts = s.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
+ if (s == null)
+ return other == null;
- var invalidUrlPatterns = new List
- {
- @"[#%&:<>/{0}]".FormatWith(allowTokens ? null : @"\\\+\{\}?\*"),
- @"\.\.",
- @"\.$",
- @"^ ",
- @" $"
- };
+ return s.Equals(other, StringComparison.OrdinalIgnoreCase);
+ }
- var invalidUrlPattern = String.Join("|", invalidUrlPatterns);
+ public static string ValueOr(this string s, string otherValue)
+ {
+ if (s.HasValue())
+ return s;
- return !urlParts.Any(p => Regex.IsMatch(p, invalidUrlPattern));
+ return otherValue;
}
}
}
From 81738b4a55b1b7bdc02b305e072b4a2f9a551929 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 12 Mar 2013 12:45:57 -0600
Subject: [PATCH 87/97] allowing default constraints to apply to query params.
---
src/AttributeRouting/Framework/RouteBuilder.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index e345e1a..d9808d9 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -273,12 +273,13 @@ private void CreateRouteConstraints(RouteSpecification routeSpec, out IDictionar
}
} // ... go to next parameter
- // Globally configured constraints:
+ // Globally configured constraints - have to treat path params differently than query params.
var detokenizedUrl = DetokenizeUrl(tokenizedUrl);
string path, query;
detokenizedUrl.GetPathAndQuery(out path, out query);
var urlPathParameterNames = GetUrlParameterContents(path).ToArray();
var urlQueryParameterNames = GetUrlParameterContents(query).ToArray();
+
foreach (var defaultConstraint in _configuration.DefaultRouteConstraints)
{
ApplyDefaultRouteConstraint(urlPathParameterNames, defaultConstraint, constraints, false);
From bd27d42f869fffe523751dd478cb8350b89ce356 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 12 Mar 2013 12:56:58 -0600
Subject: [PATCH 88/97] added query string constraints to output of route
logger.
---
.../Logging/LoggingExtensions.cs | 3 ++-
.../Logging/LogRoutesHandler.cs | 15 ++++++++-------
.../Logging/LoggingExtensions.cs | 1 +
src/AttributeRouting/Logging/RouteLoggingInfo.cs | 9 +++++++++
4 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/src/AttributeRouting.Web.Http.SelfHost/Logging/LoggingExtensions.cs b/src/AttributeRouting.Web.Http.SelfHost/Logging/LoggingExtensions.cs
index 5e6a3e9..d8c07ad 100644
--- a/src/AttributeRouting.Web.Http.SelfHost/Logging/LoggingExtensions.cs
+++ b/src/AttributeRouting.Web.Http.SelfHost/Logging/LoggingExtensions.cs
@@ -24,8 +24,9 @@ public static void LogTo(this HttpRoute route, TextWriter writer)
var attributeRoute = route as IAttributeRoute;
var info = RouteLoggingInfo.GetRouteInfo(route.RouteTemplate,
route.Defaults,
+ attributeRoute.SafeGet(r => r.QueryStringDefaults),
route.Constraints,
- attributeRoute.SafeGet(x => x.QueryStringConstraints),
+ attributeRoute.SafeGet(r => r.QueryStringConstraints),
route.DataTokens);
LogWriter.LogRoute(writer, route.RouteTemplate, info);
diff --git a/src/AttributeRouting.Web/Logging/LogRoutesHandler.cs b/src/AttributeRouting.Web/Logging/LogRoutesHandler.cs
index a630758..9e7472a 100644
--- a/src/AttributeRouting.Web/Logging/LogRoutesHandler.cs
+++ b/src/AttributeRouting.Web/Logging/LogRoutesHandler.cs
@@ -95,13 +95,14 @@ private string GetOutput()
private static IEnumerable GetRouteInfo()
{
- return from r in RouteTable.Routes.OfType()
- let ar = r as IAttributeRoute
- let routeInfo = RouteLoggingInfo.GetRouteInfo(r.Url,
- r.Defaults,
- r.Constraints,
- ar.SafeGet(x => x.QueryStringConstraints),
- r.DataTokens)
+ return from route in RouteTable.Routes.OfType()
+ let attributeRoute = route as IAttributeRoute
+ let routeInfo = RouteLoggingInfo.GetRouteInfo(route.Url,
+ route.Defaults,
+ attributeRoute.SafeGet(r => r.QueryStringDefaults),
+ route.Constraints,
+ attributeRoute.SafeGet(r => r.QueryStringConstraints),
+ route.DataTokens)
select new
{
methods = routeInfo.HttpMethods,
diff --git a/src/AttributeRouting.Web/Logging/LoggingExtensions.cs b/src/AttributeRouting.Web/Logging/LoggingExtensions.cs
index a6e1ad9..c64db19 100644
--- a/src/AttributeRouting.Web/Logging/LoggingExtensions.cs
+++ b/src/AttributeRouting.Web/Logging/LoggingExtensions.cs
@@ -28,6 +28,7 @@ public static void LogTo(this Route route, TextWriter writer)
var name = attributeRoute.SafeGet(r => r.RouteName);
var info = RouteLoggingInfo.GetRouteInfo(route.Url,
route.Defaults,
+ attributeRoute.SafeGet(r => r.QueryStringDefaults),
route.Constraints,
attributeRoute.SafeGet(r => r.QueryStringConstraints),
route.DataTokens);
diff --git a/src/AttributeRouting/Logging/RouteLoggingInfo.cs b/src/AttributeRouting/Logging/RouteLoggingInfo.cs
index aa2d93a..7354cce 100644
--- a/src/AttributeRouting/Logging/RouteLoggingInfo.cs
+++ b/src/AttributeRouting/Logging/RouteLoggingInfo.cs
@@ -23,6 +23,7 @@ public RouteLoggingInfo()
public static RouteLoggingInfo GetRouteInfo(string url,
IDictionary defaults,
+ IDictionary queryStringDefaults,
IDictionary constraints,
IDictionary queryStringConstraints,
IDictionary dataTokens)
@@ -41,6 +42,14 @@ public static RouteLoggingInfo GetRouteInfo(string url,
item.Defaults.Add(@default.Key, defaultValue.ValueOr("Optional"));
}
}
+ if (queryStringDefaults != null)
+ {
+ foreach (var @default in queryStringDefaults)
+ {
+ var defaultValue = @default.Value.ToString();
+ item.Defaults.Add(@default.Key, defaultValue);
+ }
+ }
//************************
// Constraints
From e092e927b914f78d6aca45e2a163ea53371ddb3b Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 12 Mar 2013 15:05:49 -0600
Subject: [PATCH 89/97] #219 - fixed just introduced bug with handling of
querystring defaults.
---
.../Features/RouteConstraints.feature | 11 +--
.../Features/RouteConstraints.feature.cs | 1 +
.../InlineRouteConstraintsControllers.cs | 6 ++
.../Constraints/OptionalRouteConstraint.cs | 17 +++--
.../Constraints/OptionalRouteConstraint.cs | 19 ++++--
.../Constraints/QueryStringRouteConstraint.cs | 3 +-
.../Constraints/OptionalRouteConstraint.cs | 21 ++++--
.../Constraints/QueryStringRouteConstraint.cs | 3 +-
src/AttributeRouting/AttributeRouting.csproj | 1 +
.../Framework/AttributeRouteVisitor.cs | 7 ++
.../Framework/RouteBuilder.cs | 68 +++++++++----------
.../Helpers/DictionaryExtensions.cs | 17 +++++
.../Logging/RouteLoggingInfo.cs | 37 +++-------
13 files changed, 120 insertions(+), 91 deletions(-)
create mode 100644 src/AttributeRouting/Helpers/DictionaryExtensions.cs
diff --git a/src/AttributeRouting.Specs/Features/RouteConstraints.feature b/src/AttributeRouting.Specs/Features/RouteConstraints.feature
index f4086f5..c3c5d10 100644
--- a/src/AttributeRouting.Specs/Features/RouteConstraints.feature
+++ b/src/AttributeRouting.Specs/Features/RouteConstraints.feature
@@ -158,8 +158,9 @@ Scenario Outline: Matching inline route constraints in the querystring
#When a request for "Http-Inline-Constraints/" is made
#Then the action matched
Examples:
- | url | action | condition |
- | Querystring?x=123&y=hello | Querystring | is |
- | Querystring?x=abc&y=hello | Querystring | is not |
- | Querystring?x=abc | Querystring | is not |
- | Querystring?y=hello | Querystring | is not |
+ | url | action | condition |
+ | Querystring?x=123&y=hello | Querystring | is |
+ | Querystring?x=abc&y=hello | Querystring | is not |
+ | Querystring?x=abc | Querystring | is not |
+ | Querystring?y=hello | Querystring | is not |
+ | QuerystringOptional | QuerystringOptional | is |
diff --git a/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs b/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
index 6585750..dbadea5 100644
--- a/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
+++ b/src/AttributeRouting.Specs/Features/RouteConstraints.feature.cs
@@ -315,6 +315,7 @@ public virtual void MatchingInlineRouteConstraints(string url, string action, st
[NUnit.Framework.TestCaseAttribute("Querystring?x=abc&y=hello", "Querystring", "is not", null)]
[NUnit.Framework.TestCaseAttribute("Querystring?x=abc", "Querystring", "is not", null)]
[NUnit.Framework.TestCaseAttribute("Querystring?y=hello", "Querystring", "is not", null)]
+ [NUnit.Framework.TestCaseAttribute("QuerystringOptional", "QuerystringOptional", "is", null)]
public virtual void MatchingInlineRouteConstraintsInTheQuerystring(string url, string action, string condition, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Matching inline route constraints in the querystring", exampleTags);
diff --git a/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs b/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs
index 1d09e65..46aae18 100644
--- a/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs
+++ b/src/AttributeRouting.Specs/Subjects/InlineRouteConstraintsControllers.cs
@@ -33,6 +33,12 @@ public string Querystring(int x, string y)
return "";
}
+ [GET("QuerystringOptional?{x:int?}")]
+ public string QuerystringOptional(int x, string y)
+ {
+ return "";
+ }
+
[GET("Alpha/{x:alpha}")]
public string Alpha(string x)
{
diff --git a/src/AttributeRouting.Web.Http.WebHost/Constraints/OptionalRouteConstraint.cs b/src/AttributeRouting.Web.Http.WebHost/Constraints/OptionalRouteConstraint.cs
index 5d07c98..afbcc22 100644
--- a/src/AttributeRouting.Web.Http.WebHost/Constraints/OptionalRouteConstraint.cs
+++ b/src/AttributeRouting.Web.Http.WebHost/Constraints/OptionalRouteConstraint.cs
@@ -1,7 +1,7 @@
using System.Web;
using System.Web.Http;
using System.Web.Routing;
-using AttributeRouting.Constraints;
+using AttributeRouting.Constraints;
using AttributeRouting.Helpers;
namespace AttributeRouting.Web.Http.WebHost.Constraints
@@ -21,13 +21,18 @@ public object Constraint
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
- {
+ {
+ // NOTE: Due to webhost hijacking the route added to the route collection, this route is NOT an IAttributeRoute.
+ // As such we cannot check the querystring defaults as we do in the other impls of IOptionalRouteConstraint.
+ var allDefaults = route.Defaults;
+
// If the param is optional and has no value, then pass the constraint
- if (route.Defaults.ContainsKey(parameterName)
- && route.Defaults[parameterName] == RouteParameter.Optional)
+ if (allDefaults.ContainsKey(parameterName) && allDefaults[parameterName] == RouteParameter.Optional)
{
- if (values[parameterName].HasNoValue())
- return true;
+ if (values[parameterName].HasNoValue())
+ {
+ return true;
+ }
}
return _constraint.Match(httpContext, route, parameterName, values, routeDirection);
diff --git a/src/AttributeRouting.Web.Http/Constraints/OptionalRouteConstraint.cs b/src/AttributeRouting.Web.Http/Constraints/OptionalRouteConstraint.cs
index e2a5500..e8ee2ed 100644
--- a/src/AttributeRouting.Web.Http/Constraints/OptionalRouteConstraint.cs
+++ b/src/AttributeRouting.Web.Http/Constraints/OptionalRouteConstraint.cs
@@ -2,7 +2,8 @@
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Routing;
-using AttributeRouting.Constraints;
+using AttributeRouting.Constraints;
+using AttributeRouting.Framework;
using AttributeRouting.Helpers;
namespace AttributeRouting.Web.Http.Constraints
@@ -22,13 +23,19 @@ public object Constraint
}
public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection)
- {
+ {
+ var attributeRoute = (IAttributeRoute)route;
+ var allDefaults = new Dictionary();
+ allDefaults.Merge(attributeRoute.Defaults);
+ allDefaults.Merge(attributeRoute.QueryStringDefaults);
+
// If the param is optional and has no value, then pass the constraint
- if (route.Defaults.ContainsKey(parameterName)
- && route.Defaults[parameterName] == RouteParameter.Optional)
+ if (allDefaults.ContainsKey(parameterName) && allDefaults[parameterName] == RouteParameter.Optional)
{
- if (values[parameterName].HasNoValue())
- return true;
+ if (values[parameterName].HasNoValue())
+ {
+ return true;
+ }
}
return _constraint.Match(request, route, parameterName, values, routeDirection);
diff --git a/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraint.cs b/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraint.cs
index 6a05ce3..c007901 100644
--- a/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraint.cs
+++ b/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraint.cs
@@ -1,5 +1,4 @@
-using System.Collections.Generic;
-using System.Linq;
+using System.Collections.Generic;
using System.Net.Http;
using System.Web;
using System.Web.Http.Routing;
diff --git a/src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraint.cs b/src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraint.cs
index e6fc342..6d3d4e9 100644
--- a/src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraint.cs
+++ b/src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraint.cs
@@ -1,7 +1,9 @@
+using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using AttributeRouting.Constraints;
+using AttributeRouting.Framework;
using AttributeRouting.Helpers;
namespace AttributeRouting.Web.Mvc.Constraints
@@ -21,14 +23,19 @@ public object Constraint
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
- {
- // If the param is optional and has no value, then pass the constraint
- if (route.Defaults.ContainsKey(parameterName)
- && route.Defaults[parameterName] == UrlParameter.Optional)
+ {
+ var attributeRoute = (IAttributeRoute)route;
+ var allDefaults = new Dictionary();
+ allDefaults.Merge(attributeRoute.Defaults);
+ allDefaults.Merge(attributeRoute.QueryStringDefaults);
+
+ // If the param is optional and has no value, then pass the constraint
+ if (allDefaults.ContainsKey(parameterName) && allDefaults[parameterName] == UrlParameter.Optional)
{
- var value = values[parameterName];
- if (value == UrlParameter.Optional || value.HasNoValue())
- return true;
+ if (values[parameterName].HasNoValue())
+ {
+ return true;
+ }
}
return _constraint.Match(httpContext, route, parameterName, values, routeDirection);
diff --git a/src/AttributeRouting.Web/Constraints/QueryStringRouteConstraint.cs b/src/AttributeRouting.Web/Constraints/QueryStringRouteConstraint.cs
index e038015..c9de73f 100644
--- a/src/AttributeRouting.Web/Constraints/QueryStringRouteConstraint.cs
+++ b/src/AttributeRouting.Web/Constraints/QueryStringRouteConstraint.cs
@@ -22,8 +22,7 @@ public object Constraint
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
// If the query param does not exist in the query or the route defaults, then fail.
- var queryString = httpContext.Request.QueryString;
- var value = queryString[parameterName] ?? values[parameterName];
+ var value = httpContext.Request.QueryString[parameterName] ?? values[parameterName];
if (value.HasNoValue())
{
return false;
diff --git a/src/AttributeRouting/AttributeRouting.csproj b/src/AttributeRouting/AttributeRouting.csproj
index e666f46..c591f85 100644
--- a/src/AttributeRouting/AttributeRouting.csproj
+++ b/src/AttributeRouting/AttributeRouting.csproj
@@ -92,6 +92,7 @@
+
diff --git a/src/AttributeRouting/Framework/AttributeRouteVisitor.cs b/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
index e822cc3..cac2e00 100644
--- a/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
+++ b/src/AttributeRouting/Framework/AttributeRouteVisitor.cs
@@ -60,6 +60,13 @@ public void AddQueryStringDefaultsToRouteValues(IDictionary rout
{
foreach (var queryStringDefault in _route.QueryStringDefaults)
{
+ // Don't add optional params.
+ if (queryStringDefault.Value.HasNoValue())
+ {
+ continue;
+ }
+
+ // Add default if no value is present in the current route values.
if (!routeValues.ContainsKey(queryStringDefault.Key))
{
routeValues.Add(queryStringDefault.Key, queryStringDefault.Value);
diff --git a/src/AttributeRouting/Framework/RouteBuilder.cs b/src/AttributeRouting/Framework/RouteBuilder.cs
index d9808d9..8640d9f 100644
--- a/src/AttributeRouting/Framework/RouteBuilder.cs
+++ b/src/AttributeRouting/Framework/RouteBuilder.cs
@@ -14,13 +14,20 @@ namespace AttributeRouting.Framework
public class RouteBuilder
{
private static readonly Regex ConstraintParamsRegex = new Regex(@"^.*\(.*\)$");
+
+ private static readonly Regex DetokenizeUrlParamContentsRegex
+ = new Regex(@"(" +
+ @":.+" + // inline constraints
+ @"|=.+" + // or inline defaults
+ @"|\?" + // or inline optionals
+ @")$" // at end of string
+ );
private static readonly Regex DetokenizeUrlRegex =
- new Regex(@"(?<=\{)\?" + // leading question mark (used to specify optional param)
- @"|\?(?=\})" + // trailing question mark (used to specify optional param)
- @"|\(.*?\)(?=\})" + // stuff inside parens (used to specify inline regex route constraint)
- @"|\:(.*?)(\(.*?\))?((?=\})|(?=\?\}))" + // new inline constraint syntax
- @"|(?<=\{.*)=.*?(?=\})" // equals and value (used to specify inline parameter default value)
+ new Regex(@"\?(?=\})" + // trailing question mark (used to specify optional param)
+ @"|\(.*?\)(?=\})" + // or stuff inside parens (used to specify inline regex route constraint)
+ @"|\:(.*?)(\(.*?\))?((?=\})|(?=\?\}))" + // or new inline constraint syntax
+ @"|(?<=\{.*)=.*?(?=\})" // or equals and value (used to specify inline parameter default value)
);
private readonly ConfigurationBase _configuration;
@@ -331,47 +338,38 @@ private void CreateRouteDefaults(RouteSpecification routeSpec, out IDictionary p.EndsWith("?")))
+ // Inspect the url path for optional parameters and default values.
+ foreach (var parameter in urlParameters)
{
- // Strip off any optional tokens.
- var parameterName = parameter.TrimEnd('?');
-
- // Strip off any inline constraints.
- if (parameterName.Contains(':'))
- {
- parameterName = parameterName.Substring(0, parameterName.IndexOf(':'));
- }
-
- // Do not override default defaults.
- if (defaults.ContainsKey(parameterName))
- {
- continue;
+ // Does this param have a default value or is it optional?
+ var isOptional = parameter.EndsWith("?");
+ var indexOfEquals = parameter.IndexOf('=');
+ if (!isOptional && indexOfEquals == -1)
+ {
+ continue;
}
- defaults.Add(parameterName, _parameterFactory.Optional());
- }
-
- // Inline defaults
- foreach (var parameter in urlParameters.Where(p => p.Contains('=')))
- {
- var indexOfEquals = parameter.IndexOf('=');
- var parameterName = parameter.Substring(0, indexOfEquals);
+ // Keep track if this is a querystring param
var parameterIsInQueryString = queryStringParameters.Contains(parameter);
- // Strip off inline constraints
- if (parameterName.Contains(':'))
- {
- parameterName = parameterName.Substring(0, parameterName.IndexOf(':'));
- }
+ // Strip off inline constraints, defaults, and optional tokens.
+ var parameterName = DetokenizeUrlParamContentsRegex.Replace(parameter, "");
// Do not override default defaults.
if (defaults.ContainsKey(parameterName))
{
continue;
+ }
+
+ object defaultValue;
+ if (isOptional)
+ {
+ defaultValue = _parameterFactory.Optional();
+ }
+ else // It has a default value.
+ {
+ defaultValue = parameter.Substring(indexOfEquals + 1, parameter.Length - indexOfEquals - 1);
}
-
- var defaultValue = parameter.Substring(indexOfEquals + 1, parameter.Length - indexOfEquals - 1);
if (parameterIsInQueryString)
{
diff --git a/src/AttributeRouting/Helpers/DictionaryExtensions.cs b/src/AttributeRouting/Helpers/DictionaryExtensions.cs
new file mode 100644
index 0000000..f34b591
--- /dev/null
+++ b/src/AttributeRouting/Helpers/DictionaryExtensions.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+
+namespace AttributeRouting.Helpers
+{
+ public static class DictionaryExtensions
+ {
+ public static void Merge(this IDictionary target, IDictionary source)
+ {
+ if (source == null) return;
+
+ foreach (var kvp in source)
+ {
+ target.Add(kvp.Key, kvp.Value);
+ }
+ }
+ }
+}
diff --git a/src/AttributeRouting/Logging/RouteLoggingInfo.cs b/src/AttributeRouting/Logging/RouteLoggingInfo.cs
index 7354cce..8647482 100644
--- a/src/AttributeRouting/Logging/RouteLoggingInfo.cs
+++ b/src/AttributeRouting/Logging/RouteLoggingInfo.cs
@@ -34,41 +34,22 @@ public static RouteLoggingInfo GetRouteInfo(string url,
//************************
// Defaults
- if (defaults != null)
+ var allDefaults = new Dictionary();
+ allDefaults.Merge(defaults);
+ allDefaults.Merge(queryStringDefaults);
+
+ foreach (var @default in allDefaults)
{
- foreach (var @default in defaults)
- {
- var defaultValue = @default.Value.ToString();
- item.Defaults.Add(@default.Key, defaultValue.ValueOr("Optional"));
- }
- }
- if (queryStringDefaults != null)
- {
- foreach (var @default in queryStringDefaults)
- {
- var defaultValue = @default.Value.ToString();
- item.Defaults.Add(@default.Key, defaultValue);
- }
+ var defaultValue = @default.Value.ToString();
+ item.Defaults.Add(@default.Key, defaultValue);
}
//************************
// Constraints
var allConstraints = new Dictionary();
- if (constraints != null)
- {
- foreach (var constraint in constraints)
- {
- allConstraints.Add(constraint.Key, constraint.Value);
- }
- }
- if (queryStringConstraints != null)
- {
- foreach (var constraint in queryStringConstraints)
- {
- allConstraints.Add(constraint.Key, constraint.Value);
- }
- }
+ allConstraints.Merge(constraints);
+ allConstraints.Merge(queryStringConstraints);
foreach (var constraint in allConstraints)
{
From b66b1d8f38e584a789ea3e9e51daf849eee04cf2 Mon Sep 17 00:00:00 2001
From: Tim McCall
Date: Tue, 12 Mar 2013 15:08:59 -0600
Subject: [PATCH 90/97] updated readme assinfo for 3.5.6
---
README.textile | 5 +++++
src/SharedAssemblyInfo.cs | 6 +++---
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/README.textile b/README.textile
index d8da277..5925f3b 100644
--- a/README.textile
+++ b/README.textile
@@ -4,6 +4,11 @@ h2. Please refer to "attributerouting.net":http://attributerouting.net/ for docu
h3. Changelog
+_3.5.6_
+
+* #219 - BUG FIX: with handling of querystring optionals.
+* Now applying default constraints to matching query string params.
+
_3.5.5_
* #218 - BUG FIX: fixed handling of querystring default values.
diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs
index 10df449..2a0fa9b 100644
--- a/src/SharedAssemblyInfo.cs
+++ b/src/SharedAssemblyInfo.cs
@@ -7,7 +7,7 @@
[assembly: AssemblyProduct("AttributeRouting")]
[assembly: AssemblyCopyright("Copyright 2010-2013 Tim McCall")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("3.5.5")]
-[assembly: AssemblyFileVersion("3.5.5")]
-[assembly: AssemblyInformationalVersion("3.5.5")]
+[assembly: AssemblyVersion("3.5.6")]
+[assembly: AssemblyFileVersion("3.5.6")]
+[assembly: AssemblyInformationalVersion("3.5.6")]
[assembly: AssemblyConfiguration("Release")]
From ef09cf8c68128e4fc884155e2d19f584b035e082 Mon Sep 17 00:00:00 2001
From: CaptainCodeman
Date: Thu, 18 Apr 2013 20:07:22 -0600
Subject: [PATCH 91/97] Fix #241. Ignore virtual path prefix of request URL
when performing optimized checking of static left part of route
---
src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
index 5fe6203..24b06ec 100644
--- a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
+++ b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
@@ -85,7 +85,7 @@ public string Url
public override IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestMessage request)
{
// Optimize matching by comparing the static left part of the route url with the requested path.
- var requestedPath = GetCachedValue(request, RequestedPathKey, () => request.RequestUri.AbsolutePath.Substring(1));
+ var requestedPath = GetCachedValue(request, RequestedPathKey, () => request.RequestUri.AbsolutePath.Substring(virtualPathRoot.Length));
if (!_visitor.IsStaticLeftPartOfUrlMatched(requestedPath))
{
return null;
From 19fbc428cd6d727a3ac9421960c7c719c40e4ecd Mon Sep 17 00:00:00 2001
From: CaptainCodeman
Date: Fri, 19 Apr 2013 08:55:39 -0600
Subject: [PATCH 92/97] #241. Add some unit tests for virtual path handling in
HttpAttributeRoute.
---
.../Tests/BugFixTests.cs | 80 +++++++++++++++++--
1 file changed, 72 insertions(+), 8 deletions(-)
diff --git a/src/AttributeRouting.Specs/Tests/BugFixTests.cs b/src/AttributeRouting.Specs/Tests/BugFixTests.cs
index 7372780..ce1aff2 100644
--- a/src/AttributeRouting.Specs/Tests/BugFixTests.cs
+++ b/src/AttributeRouting.Specs/Tests/BugFixTests.cs
@@ -1,20 +1,23 @@
using System;
using System.Collections.Generic;
-using System.Linq;
+using System.Linq;
+using System.Net.Http;
using System.Threading;
-using System.Web.Http;
-using System.Web.Mvc;
+using System.Web.Http;
+using System.Web.Http.Routing;
using System.Web.Routing;
using AttributeRouting.Framework.Localization;
using AttributeRouting.Specs.Subjects;
using AttributeRouting.Specs.Subjects.Http;
-using AttributeRouting.Web.Http.Constraints;
+using AttributeRouting.Web.Http.Constraints;
+using AttributeRouting.Web.Http.Framework;
using AttributeRouting.Web.Http.WebHost;
using AttributeRouting.Web.Logging;
using AttributeRouting.Web.Mvc;
using MvcContrib.TestHelper;
-using NUnit.Framework;
-
+using NUnit.Framework;
+using UrlHelper = System.Web.Mvc.UrlHelper;
+
namespace AttributeRouting.Specs.Tests
{
public class BugFixTests
@@ -195,5 +198,66 @@ public void Issue191_default_web_config_inits_web_http_constraint_factory()
var inMemoryConfig = new HttpWebConfiguration();
Assert.IsAssignableFrom(inMemoryConfig.RouteConstraintFactory);
}
- }
-}
+
+ [Test]
+ public void Issue241_httpRoute_matches_request_for_route_at_root()
+ {
+ var route = BuildHttpAttributeRoute("Controller/Action", false, false);
+
+ var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Controller/Action");
+ var routeData = route.GetRouteData("/", request);
+
+ Assert.That(routeData, Is.Not.Null);
+ Assert.That(routeData.Route, Is.EqualTo(route));
+ }
+
+ [Test]
+ public void Issue241_httpRoute_doesnt_match_root_request_for_route_under_a_virtual_path()
+ {
+ var route = BuildHttpAttributeRoute("Controller/Action", false, false);
+
+ var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Controller/Action");
+ var routeData = route.GetRouteData("/virtual/", request);
+
+ Assert.That(routeData, Is.Null);
+ }
+
+ [Test]
+ public void Issue241_httpRoute_doesnt_match_virtual_path_request_for_route_at_root_path()
+ {
+ var route = BuildHttpAttributeRoute("Controller/Action", false, false);
+
+ var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/virtual/Controller/Action");
+ var routeData = route.GetRouteData("/", request);
+
+ Assert.That(routeData, Is.Null);
+ }
+
+ [Test]
+ public void Issue241_httpRoute_matches_virtual_path_request_for_route_under_a_virtual_path()
+ {
+ var route = BuildHttpAttributeRoute("Controller/Action", false, false);
+
+ var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/virtual/Controller/Action");
+ var routeData = route.GetRouteData("/virtual/", request);
+
+ Assert.That(routeData, Is.Not.Null);
+ Assert.That(routeData.Route, Is.EqualTo(route));
+ }
+
+ private HttpRoute BuildHttpAttributeRoute(string url, bool useLowercaseRoutes, bool appendTrailingSlash)
+ {
+ var configuration = new Web.Http.HttpConfiguration
+ {
+ UseLowercaseRoutes = useLowercaseRoutes,
+ AppendTrailingSlash = appendTrailingSlash,
+ };
+
+ return new HttpAttributeRoute(url,
+ new HttpRouteValueDictionary(),
+ new HttpRouteValueDictionary(),
+ new HttpRouteValueDictionary(),
+ configuration);
+ }
+ }
+}
From aaed83fe1d641a3058f5b2ae020da988ef491e91 Mon Sep 17 00:00:00 2001
From: Jeremy Read
Date: Mon, 22 Apr 2013 09:07:18 +1200
Subject: [PATCH 93/97] Issue #239
---
.../Logging/RouteLoggingInfo.cs | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/AttributeRouting/Logging/RouteLoggingInfo.cs b/src/AttributeRouting/Logging/RouteLoggingInfo.cs
index 8647482..deb104b 100644
--- a/src/AttributeRouting/Logging/RouteLoggingInfo.cs
+++ b/src/AttributeRouting/Logging/RouteLoggingInfo.cs
@@ -114,11 +114,25 @@ public static RouteLoggingInfo GetRouteInfo(string url,
{
if (token.Key.ValueEquals("namespaces"))
{
- item.DataTokens.Add(token.Key, String.Join(", ", (string[])token.Value));
+ if (token.Value is string[])
+ {
+ item.DataTokens.Add(token.Key, String.Join(", ", (string[])token.Value));
+ }
+ else
+ {
+ item.DataTokens.Add(token.Key, String.Join(", ", (new string[] { token.Value.ToString() })));
+ }
}
else if (token.Key.ValueEquals("httpMethods"))
{
- item.HttpMethods = String.Join(", ", (string[])token.Value);
+ if (token.Value is string[])
+ {
+ item.HttpMethods = String.Join(", ", (string[])token.Value);
+ }
+ else
+ {
+ item.HttpMethods = String.Join(", ", (new string[] { token.Value.ToString() }));
+ }
}
else if (!token.Key.ValueEquals("actionMethod"))
{
From 5e4359371ab4440caf8c590f0ddef4f26ed9b083 Mon Sep 17 00:00:00 2001
From: pjklein
Date: Fri, 31 May 2013 23:49:12 -0700
Subject: [PATCH 94/97] Fixed Bug 233 - Changed dependency from WebActivator
1.0.0.0 to WebActivatorEx 2.0.1.0
---
.../AttributeRouting.WebApi.Hosted.nutrans | 2 +-
nuget/AttributeRouting.WebApi/AttributeRouting.WebApi.nutrans | 2 +-
nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.cs.pp | 2 +-
nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.vb.pp | 2 +-
nuget/AttributeRouting/AttributeRouting.nutrans | 2 +-
nuget/AttributeRouting/AttributeRoutingConfig.cs.pp | 2 +-
nuget/AttributeRouting/AttributeRoutingConfig.vb.pp | 2 +-
7 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.WebApi.Hosted.nutrans b/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.WebApi.Hosted.nutrans
index ee78294..19844b3 100644
--- a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.WebApi.Hosted.nutrans
+++ b/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.WebApi.Hosted.nutrans
@@ -9,7 +9,7 @@