├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── appveyor.yml ├── build ├── ExpressiveAnnotations.dll.nuspec ├── ExpressiveAnnotations.nuspec ├── release.sh └── test.ps1 ├── doc └── api │ ├── api.chm │ └── api.shfbproj ├── extern ├── jquery-1.8.2.js ├── jquery.validate-1.10.0.js ├── jquery.validate.unobtrusive-3.1.1.js ├── qunit-1.18.0.css └── qunit-1.18.0.js ├── logo.png ├── package.json └── src ├── .nuget └── packages.config ├── ExpressiveAnnotations.MvcUnobtrusive.Tests ├── App.config ├── BaseTest.cs ├── ExpressiveAnnotations.MvcUnobtrusive.Tests.csproj ├── ProcessStorageTest.cs ├── Properties │ └── AssemblyInfo.cs ├── ProviderTest.cs ├── Resources.Designer.cs ├── Resources.pl.resx ├── Resources.resx ├── ValidatorsTest.cs └── packages.config ├── ExpressiveAnnotations.MvcUnobtrusive ├── Caching │ ├── ProcessStorage.cs │ └── RequestStorage.cs ├── ExpressiveAnnotations.MvcUnobtrusive.csproj ├── Helper.cs ├── Properties │ └── AssemblyInfo.cs ├── Providers │ └── ExpressiveAnnotationsModelValidatorProvider.cs ├── Validators │ ├── AssertThatValidator.cs │ ├── ExpressiveValidator.cs │ └── RequiredIfValidator.cs └── packages.config ├── ExpressiveAnnotations.MvcWebSample.UITests ├── App.config ├── AttribsCompileTest.cs ├── BaseTest.cs ├── ClientModeTest.cs ├── DriverFixture.cs ├── ExpressiveAnnotations.MvcWebSample.UITests.csproj ├── Helper.cs ├── HomePage.cs ├── Properties │ └── AssemblyInfo.cs ├── ServerFixture.cs ├── ServerModeTest.cs ├── packages.config └── phantomjs-license.txt ├── ExpressiveAnnotations.MvcWebSample ├── App_Start │ ├── BundleConfig.cs │ ├── FilterConfig.cs │ ├── RouteConfig.cs │ └── WebApiConfig.cs ├── Content │ ├── Site.css │ └── themes │ │ └── base │ │ ├── images │ │ ├── animated-overlay.gif │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ ├── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_2e83ff_256x240.png │ │ ├── ui-icons_454545_256x240.png │ │ ├── ui-icons_888888_256x240.png │ │ └── ui-icons_cd0a0a_256x240.png │ │ ├── jquery-ui.css │ │ ├── jquery.ui.accordion.css │ │ ├── jquery.ui.all.css │ │ ├── jquery.ui.autocomplete.css │ │ ├── jquery.ui.base.css │ │ ├── jquery.ui.button.css │ │ ├── jquery.ui.core.css │ │ ├── jquery.ui.datepicker.css │ │ ├── jquery.ui.dialog.css │ │ ├── jquery.ui.menu.css │ │ ├── jquery.ui.progressbar.css │ │ ├── jquery.ui.resizable.css │ │ ├── jquery.ui.selectable.css │ │ ├── jquery.ui.slider.css │ │ ├── jquery.ui.spinner.css │ │ ├── jquery.ui.tabs.css │ │ ├── jquery.ui.theme.css │ │ ├── jquery.ui.tooltip.css │ │ └── minified │ │ ├── images │ │ ├── animated-overlay.gif │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ ├── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_2e83ff_256x240.png │ │ ├── ui-icons_454545_256x240.png │ │ ├── ui-icons_888888_256x240.png │ │ └── ui-icons_cd0a0a_256x240.png │ │ ├── jquery-ui.min.css │ │ ├── jquery.ui.accordion.min.css │ │ ├── jquery.ui.autocomplete.min.css │ │ ├── jquery.ui.button.min.css │ │ ├── jquery.ui.core.min.css │ │ ├── jquery.ui.datepicker.min.css │ │ ├── jquery.ui.dialog.min.css │ │ ├── jquery.ui.menu.min.css │ │ ├── jquery.ui.progressbar.min.css │ │ ├── jquery.ui.resizable.min.css │ │ ├── jquery.ui.selectable.min.css │ │ ├── jquery.ui.slider.min.css │ │ ├── jquery.ui.spinner.min.css │ │ ├── jquery.ui.tabs.min.css │ │ ├── jquery.ui.theme.min.css │ │ └── jquery.ui.tooltip.min.css ├── Controllers │ ├── BaseController.cs │ ├── DefaultController.cs │ ├── HomeController.cs │ └── SystemController.cs ├── ExpressiveAnnotations.MvcWebSample.csproj ├── Global.asax ├── Global.asax.cs ├── Inheritance │ ├── CustomAssertThatAttribute.cs │ ├── CustomAssertThatValidator.cs │ ├── CustomExpressiveAnnotationsModelValidatorProvider.cs │ ├── CustomRequiredIfAttribute.cs │ └── CustomRequiredIfValidator.cs ├── Misc │ ├── CultureManager.cs │ ├── CustomToolchain.cs │ ├── Extensions.cs │ ├── IndicationsManager.cs │ ├── TriggersManager.cs │ ├── ValidationManager.cs │ └── VerbosityManager.cs ├── Models │ ├── Address.cs │ ├── Contact.cs │ └── Query.cs ├── Properties │ └── AssemblyInfo.cs ├── README.md ├── Resources.Designer.cs ├── Resources.pl.resx ├── Resources.resx ├── Scripts │ ├── jquery-1.8.2.js │ ├── jquery-ui-1.10.4.js │ ├── jquery.validate.js │ └── jquery.validate.unobtrusive.js ├── Views │ ├── Home │ │ ├── EditorTemplates │ │ │ ├── Address.cshtml │ │ │ ├── Contact.cshtml │ │ │ └── Query.cshtml │ │ └── Home.cshtml │ ├── Shared │ │ ├── DisplayTemplates │ │ │ ├── ISO8601Date.cshtml │ │ │ └── IntArray.cshtml │ │ ├── _Culture.cshtml │ │ ├── _EAOptions.cshtml │ │ ├── _Indications.cshtml │ │ ├── _Layout.cshtml │ │ └── _Validation.cshtml │ ├── Web.config │ └── _ViewStart.cshtml ├── Web.Debug.config ├── Web.Release.config ├── Web.config ├── favicon.ico ├── packages.config └── screenshot.png ├── ExpressiveAnnotations.MvvmDesktopSample ├── App.config ├── ExpressiveAnnotations.MvvmDesktopSample.csproj ├── Misc │ ├── FormatStringConverter.cs │ ├── NullableEnumerationExtension.cs │ ├── RadioButtonCheckedConverter.cs │ └── StringToBooleanConverter.cs ├── Models │ ├── AddressVM.cs │ ├── ContactVM.cs │ ├── ExpressiveVM.cs │ ├── MainWindowVM.cs │ └── QueryVM.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── README.md ├── Views │ ├── AddressView.xaml │ ├── AddressView.xaml.cs │ ├── App.xaml │ ├── App.xaml.cs │ ├── ContactView.xaml │ ├── ContactView.xaml.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── QueryView.xaml │ └── QueryView.xaml.cs ├── favicon.ico └── screenshot.png ├── ExpressiveAnnotations.Tests ├── App.config ├── AttribsTest.cs ├── ExpressiveAnnotations.Tests.csproj ├── Helper.cs ├── LexerTest.cs ├── LocationComparer.cs ├── ParserTest.cs ├── Properties │ └── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.pl.resx ├── Resources.resx ├── UtilsTest.cs └── packages.config ├── ExpressiveAnnotations.sln ├── ExpressiveAnnotations ├── Analysis │ ├── Expr.cs │ ├── Lexer.cs │ ├── Location.cs │ ├── ParseErrorException.cs │ ├── Parser.cs │ ├── Token.cs │ ├── TokenType.cs │ ├── TypeAdapter.cs │ └── TypeWall.cs ├── Attributes │ ├── AssertThatAttribute.cs │ ├── ExpressiveAttribute.cs │ ├── RequiredIfAttribute.cs │ └── ValueParserAttribute.cs ├── ExpressiveAnnotations.csproj ├── Functions │ ├── FunctionsManager.cs │ ├── IFunctionsManager.cs │ ├── IFunctionsProvider.cs │ └── Toolchain.cs ├── Helper.cs ├── MessageFormatter.cs └── Properties │ └── AssemblyInfo.cs ├── expressive.annotations.validate.js ├── expressive.annotations.validate.min.js ├── expressive.annotations.validate.test.js ├── form.tests.harness.html ├── form.tests.harness.latestdeps.html ├── form.tests.html └── form.tests.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sh text eol=lf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | x64/ 20 | *_i.c 21 | *_p.c 22 | *.ilk 23 | *.meta 24 | *.obj 25 | *.pch 26 | *.pdb 27 | *.pgc 28 | *.pgd 29 | *.rsp 30 | *.sbr 31 | *.tlb 32 | *.tli 33 | *.tlh 34 | *.tmp 35 | *.log 36 | *.vspscc 37 | *.vssscc 38 | .builds 39 | 40 | # Visual C++ cache files 41 | ipch/ 42 | *.aps 43 | *.ncb 44 | *.opensdf 45 | *.sdf 46 | 47 | # Visual Studio profiler 48 | *.psess 49 | *.vsp 50 | *.vspx 51 | 52 | # Guidance Automation Toolkit 53 | *.gpState 54 | 55 | # ReSharper is a .NET coding add-in 56 | _ReSharper* 57 | 58 | # NCrunch 59 | *.ncrunch* 60 | .*crunch*.local.xml 61 | 62 | # Installshield output folder 63 | [Ee]xpress 64 | 65 | # DocProject is a documentation generator add-in 66 | DocProject/buildhelp/ 67 | DocProject/Help/*.HxT 68 | DocProject/Help/*.HxC 69 | DocProject/Help/*.hhc 70 | DocProject/Help/*.hhk 71 | DocProject/Help/*.hhp 72 | DocProject/Help/Html2 73 | DocProject/Help/html 74 | 75 | # Click-Once directory 76 | publish 77 | 78 | # Publish Web Output 79 | *.Publish.xml 80 | 81 | # NuGet Packages Directory 82 | packages 83 | 84 | # Windows Azure Build Output 85 | csx 86 | *.build.csdef 87 | 88 | # Windows Store app package directory 89 | AppPackages/ 90 | 91 | # Others 92 | [Bb]in 93 | [Oo]bj 94 | sql 95 | TestResults 96 | [Tt]est[Rr]esult* 97 | *.Cache 98 | ClientBin 99 | [Ss]tyle[Cc]op.* 100 | ~$* 101 | *.dbmdl 102 | Generated_Code #added for RIA/Silverlight projects 103 | 104 | # Backup & report files from converting an old project file to a newer 105 | # Visual Studio version. Backup files are not needed, because we have git ;-) 106 | _UpgradeReport_Files/ 107 | Backup*/ 108 | UpgradeLog*.XML 109 | 110 | # Linked content script files (copied automatically before each build) 111 | /src/ExpressiveAnnotations.MvcWebSample/Scripts/expressive.annotations.validate*.js 112 | /bower_components 113 | /node_modules 114 | /build/[0-9]* 115 | /build/nuget.exe 116 | /build/*.html 117 | /build/*.lcov 118 | /build/*.xml 119 | /build/*.js 120 | /src/_Chutzpah.* 121 | /.vs 122 | /src/ExpressiveAnnotations.MvcWebSample/Properties/PublishProfiles/FolderProfile.pubxml 123 | /src/.vs -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jarosław Waliszko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | init: 3 | - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) 4 | branches: 5 | only: 6 | - master 7 | configuration: Release 8 | before_build: 9 | - ps: nuget restore src\ExpressiveAnnotations.sln -verbosity quiet 10 | build: 11 | project: src\ExpressiveAnnotations.sln 12 | parallel: true 13 | verbosity: minimal 14 | image: Visual Studio 2017 15 | test_script: 16 | - "SET NODE_PATH=%AppData%\\npm\\node_modules" 17 | - ps: build\test.ps1 18 | - "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%" 19 | - pip install codecov 20 | - codecov --no-color -f "csharp-coverage.xml" "javascript-coverage.lcov" 21 | install: 22 | - choco install firefox --version 46.0.1 --x86 23 | - npm install uglify-js -g -------------------------------------------------------------------------------- /build/ExpressiveAnnotations.dll.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ExpressiveAnnotations.dll 5 | $version$ 6 | Server-side component of ExpressiveAnnotations 7 | Jarosław Waliszko 8 | Jarosław Waliszko 9 | https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/master/logo.png 10 | https://github.com/jwaliszko/ExpressiveAnnotations/blob/master/LICENSE 11 | https://github.com/jwaliszko/ExpressiveAnnotations 12 | false 13 | ExpressiveAnnotations is a small .NET and JavaScript library which provides full-stack annotation-based conditional validation mechanisms. 14 | 15 | Given attributes, powered by expressions engine, allow to forget about imperative way of step-by-step implementation of validation conditions in many cases. Since fields validation requirements are applied as metadata, domain-related code is more condensed. 16 | 17 | This package contains single component, i.e. ExpressiveAnnotations.dll - assembly with validation attributes powered by expressions engine. 18 | Server-side component of ExpressiveAnnotations - annotation-based conditional validation library. 19 | https://github.com/jwaliszko/ExpressiveAnnotations/releases 20 | en-US 21 | DataAnnotations Validation 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /build/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -ne 2 ]; then 4 | echo "usage: release.sh product-version keyfile-path" 5 | exit 1 6 | fi 7 | 8 | if [ ! -f nuget.exe ]; then 9 | echo -e "\n------ nuget.exe downloading..." 10 | wget -nv 'http://dist.nuget.org/win-x86-commandline/latest/nuget.exe' 11 | fi 12 | 13 | productVersion=$1 14 | keyfile=$2 15 | msbuild="/cygdrive/c/Program Files (x86)/Microsoft Visual Studio/2017/Community/MSBuild/15.0/Bin/MSBuild.exe" 16 | visualStudioVersion=15.0 17 | targets=( v4.0 v4.5 ) 18 | 19 | rm -fr $productVersion 20 | 21 | echo -e "\n------ package restoration..." 22 | ./nuget.exe restore ../src/ExpressiveAnnotations.sln -verbosity quiet 23 | 24 | if [ $? -ne 0 ]; then 25 | exit 1 26 | fi 27 | 28 | for target in "${targets[@]}" 29 | do 30 | const="NET"`echo $target | sed 's/[.v]//g'` 31 | 32 | echo -e "\n------ release $const compilation..." 33 | "$msbuild" /nologo ../src/ExpressiveAnnotations.MvcUnobtrusive/ExpressiveAnnotations.MvcUnobtrusive.csproj "/t:Clean;Rebuild" /p:BuildProjectReferences=true /p:Configuration=Release /p:PlatformTarget=AnyCPU /p:AssemblyOriginatorKeyFile=$keyfile /p:TreatWarningsAsErrors=true /p:TargetFrameworkVersion=$target "/p:DefineConstants=$const SIGNED" /p:RunCodeAnalysis=False /p:SignAssembly=True /p:VisualStudioVersion=$visualStudioVersion /verbosity:minimal 34 | 35 | if [ $? -ne 0 ]; then 36 | exit 1 37 | fi 38 | 39 | dist=`echo $const | tr '[:upper:]' '[:lower:]'` 40 | mkdir -p $dist 41 | cp ../src/ExpressiveAnnotations.MvcUnobtrusive/bin/Release/ExpressiveAnnotations* $dist 42 | done 43 | 44 | echo -e "\n------ help generation..." 45 | "$msbuild" /nologo ../doc/api/api.shfbproj /t:Build /p:Configuration=Release /verbosity:minimal 46 | 47 | if [ $? -ne 0 ]; then 48 | exit 1 49 | fi 50 | 51 | echo -e "\n------ script preparation..." 52 | sed '/!debug section enter/,/!debug section leave/{N;d;}' ../src/expressive.annotations.validate.js > expressive.annotations.validate.js 53 | uglifyjs --compress --mangle --comments /Copyright/ --output expressive.annotations.validate.min.js -- expressive.annotations.validate.js 54 | cp expressive.annotations.validate.min.js ../src/expressive.annotations.validate.min.js 55 | 56 | echo -e "\n------ package building..." 57 | coreVersion=`strings ../src/ExpressiveAnnotations/bin/Release/ExpressiveAnnotations.dll | egrep '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | cut -f1,2,3 -d"."` 58 | ./nuget.exe pack ExpressiveAnnotations.nuspec -version $productVersion -symbols 59 | ./nuget.exe pack ExpressiveAnnotations.dll.nuspec -version $coreVersion 60 | 61 | scriptVersion=`strings ../src/expressive.annotations.validate.js | egrep -o -m1 '[0-9]+\.[0-9]+\.[0-9]+'` 62 | sed -E 's/\$version\$/'$scriptVersion'/' ../package.json > package.json 63 | 64 | unobVersion=`strings ../src/ExpressiveAnnotations.MvcUnobtrusive/bin/Release/ExpressiveAnnotations.MvcUnobtrusive.dll | egrep '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | cut -f1,2,3 -d"."` 65 | 66 | echo "EA v"$productVersion" (full-stack product version) consists of the following components: 67 | ExpressiveAnnotations.dll v"$coreVersion", ExpressiveAnnotations.MvcUnobtrusive.dll v"$unobVersion", expressive.annotations.validate.js v"$scriptVersion"" > NOTES 68 | unix2dos NOTES 69 | 70 | echo -e "\n------ archive creation..." 71 | mkdir -p $productVersion/ExpressiveAnnotations/dist 72 | mv net* $productVersion/ExpressiveAnnotations/dist 73 | mv expressive.annotations.validate* $productVersion/ExpressiveAnnotations/dist 74 | mv NOTES $productVersion/ExpressiveAnnotations 75 | cp ../doc/api/api.chm $productVersion/ExpressiveAnnotations 76 | cp ../README.md $productVersion/ExpressiveAnnotations 77 | cp ../LICENSE $productVersion/ExpressiveAnnotations 78 | cp ../logo.png $productVersion/ExpressiveAnnotations 79 | mv *.nupkg $productVersion 80 | mv *.json $productVersion 81 | cd $productVersion 82 | zip -r ExpressiveAnnotations.zip ExpressiveAnnotations 83 | cd .. 84 | 85 | # nuget setapikey ... 86 | # nuget push ExpressiveAnnotations.x.x.x.nupkg -source https://www.nuget.org 87 | # nuget push ExpressiveAnnotations.dll.x.x.x.nupkg -source https://www.nuget.org 88 | -------------------------------------------------------------------------------- /doc/api/api.chm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/doc/api/api.chm -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/logo.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expressive-annotations-validate", 3 | "version": "$version$", 4 | "license": "MIT", 5 | "homepage": "https://github.com/jwaliszko/ExpressiveAnnotations", 6 | "description": "Client-side component of ExpressiveAnnotations", 7 | "author": "Jarosław Waliszko ", 8 | "main": "dist/expressive.annotations.validate.js", 9 | "files": [ 10 | "dist/expressive.annotations.validate.js", 11 | "dist/expressive.annotations.validate.min.js" 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "git://github.com/jwaliszko/ExpressiveAnnotations.git" 16 | }, 17 | "dependencies": { 18 | "jquery": ">=1.8.2", 19 | "jquery-validation-unobtrusive": ">=3.1.1", 20 | "jquery-validation": ">=1.10.0" 21 | }, 22 | "keywords": [ 23 | "validation", 24 | "mvc", 25 | "aspnetmvc", 26 | "dataannotations" 27 | ] 28 | } -------------------------------------------------------------------------------- /src/.nuget/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive.Tests/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive.Tests/BaseTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.IO; 4 | using System.Linq.Expressions; 5 | using System.Threading; 6 | using System.Web; 7 | using System.Web.Mvc; 8 | using System.Web.Routing; 9 | using ExpressiveAnnotations.MvcUnobtrusive.Caching; 10 | using Moq; 11 | 12 | namespace ExpressiveAnnotations.MvcUnobtrusive.Tests 13 | { 14 | public class BaseTest 15 | { 16 | public BaseTest() 17 | { 18 | HttpContext.Current = new HttpContext( 19 | new HttpRequest(string.Empty, "http://tempuri.org", string.Empty), 20 | new HttpResponse(new StringWriter()) 21 | ); 22 | 23 | ProcessStorage.Clear(); 24 | } 25 | 26 | protected ModelMetadata GetModelMetadata(TModel model, Expression> expression) 27 | { 28 | var property = ((MemberExpression) expression.Body).Member.Name; 29 | return new ModelMetadata(ModelMetadataProviders.Current, typeof(TModel), () => model, typeof(TProp), property); 30 | } 31 | 32 | protected ControllerContext GetControllerContext() 33 | { 34 | var request = new Mock(); 35 | request.Setup(r => r.HttpMethod).Returns("GET"); 36 | var mockHttpContext = new Mock(); 37 | mockHttpContext.Setup(c => c.Request).Returns(request.Object); 38 | var controllerContext = new ControllerContext(mockHttpContext.Object, new RouteData(), new Mock().Object); 39 | return controllerContext; 40 | } 41 | 42 | protected void CulturalExecutionUI(Action action, string culture) 43 | { 44 | var temp = Thread.CurrentThread.CurrentUICulture; // backup current UI culture 45 | Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(culture); 46 | action(); 47 | Thread.CurrentThread.CurrentUICulture = temp; // restore culture 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("ExpressiveAnnotations.MvcUnobtrusive.Tests")] 8 | [assembly: AssemblyCopyright("Copyright © Jarosław Waliszko 2014")] 9 | [assembly: AssemblyProduct("ExpressiveAnnotations.MvcUnobtrusive.Tests")] 10 | 11 | // Setting ComVisible to false makes the types in this assembly not visible 12 | // to COM components. If you need to access a type in this assembly from 13 | // COM, set the ComVisible attribute to true on that type. 14 | [assembly: ComVisible(false)] 15 | 16 | // The following GUID is for the ID of the typelib if this project is exposed to COM 17 | [assembly: Guid("1888c82a-a4b7-4352-b664-df8f695466b3")] 18 | 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Build and Revision Numbers 27 | // by using the '*' as shown below: 28 | // [assembly: AssemblyVersion("1.0.*")] 29 | [assembly: AssemblyVersion("1.0.0.0")] 30 | [assembly: AssemblyFileVersion("1.0.0.0")] 31 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive.Tests/ProviderTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ExpressiveAnnotations.Attributes; 3 | using ExpressiveAnnotations.MvcUnobtrusive.Providers; 4 | using ExpressiveAnnotations.MvcUnobtrusive.Validators; 5 | using Xunit; 6 | 7 | namespace ExpressiveAnnotations.MvcUnobtrusive.Tests 8 | { 9 | public class ProviderTest: BaseTest 10 | { 11 | [Fact] 12 | public void validators_ordered_by_ascending_priorities() 13 | { 14 | var model = new Model(); 15 | var controllerContext = GetControllerContext(); 16 | var provider = new ExpressiveAnnotationsModelValidatorProvider(); 17 | var validators = provider.GetValidators(GetModelMetadata(model, m => m.Value), controllerContext).ToList(); 18 | 19 | var exps = validators.Where(x => x is RequiredIfValidator) 20 | .Select(x => x.GetClientValidationRules() 21 | .Single().ValidationParameters["expression"].ToString() 22 | .Count(c => c == '!')) 23 | .ToList(); 24 | 25 | Assert.Equal(4, exps.Count); 26 | Assert.Equal(1, exps[0]); 27 | Assert.Equal(0, exps[1]); 28 | Assert.Equal(3, exps[2]); 29 | Assert.Equal(2, exps[3]); 30 | 31 | exps = validators.Where(x => x is AssertThatValidator) 32 | .Select(x => x.GetClientValidationRules() 33 | .Single().ValidationParameters["expression"].ToString() 34 | .Count(c => c == '!')) 35 | .ToList(); 36 | 37 | Assert.Equal(4, exps.Count); 38 | Assert.Equal(1, exps[0]); 39 | Assert.Equal(0, exps[1]); 40 | Assert.Equal(3, exps[2]); 41 | Assert.Equal(2, exps[3]); 42 | } 43 | 44 | private class Model 45 | { 46 | [RequiredIf("true", Priority = 0)] 47 | [RequiredIf("!true", Priority = -1)] 48 | [RequiredIf("!!true")] // no priority defind - will be moved at the and of its group 49 | [RequiredIf("!!!true", Priority = 1)] 50 | [AssertThat("true", Priority = 0)] 51 | [AssertThat("!true", Priority = -1)] 52 | [AssertThat("!!true")] // no priority defind - will be moved at the and of its group 53 | [AssertThat("!!!true", Priority = 1)] 54 | public int? Value { get; set; } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive.Tests/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ExpressiveAnnotations.MvcUnobtrusive.Tests { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | public class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | public static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ExpressiveAnnotations.MvcUnobtrusive.Tests.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | public static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to default. 65 | /// 66 | public static string Lang { 67 | get { 68 | return ResourceManager.GetString("Lang", resourceCulture); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive/Caching/ProcessStorage.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | using System; 6 | using System.Collections.Concurrent; 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | 10 | namespace ExpressiveAnnotations.MvcUnobtrusive.Caching 11 | { 12 | /// 13 | /// Stores arbitrary key-value pairs for the entire lifespan of the application (until the pool recycles). 14 | /// 15 | /// 16 | /// Type is thread safe (atomic invocation of the value factory func guaranteed, implementation uses concurrent dictionary with lazy value access). 17 | /// 18 | internal static class ProcessStorage // note about static generics: http://stackoverflow.com/q/3037203/270315 19 | { 20 | private static readonly ConcurrentDictionary> _cache = new ConcurrentDictionary>(); // why lazy? -> http://stackoverflow.com/q/12611167/270315 21 | 22 | public static TValue GetOrAdd(TKey key, Func valueFactory) // delegate value factory invocation guaranteed to be atomic 23 | { 24 | var lazyResult = _cache.GetOrAdd( 25 | key, 26 | k => new Lazy( 27 | () => valueFactory(k), 28 | LazyThreadSafetyMode.ExecutionAndPublication)); 29 | return lazyResult.Value; /* From http://bit.ly/2b8E1AS: If multiple concurrent threads try to call GetOrAdd with the same key at once, multiple Lazy objects may be 30 | * created but these are cheap, and all but one will be thrown away. The return Lazy object will be the same across all threads, and the 31 | * first one to call the Value property will run the expensive delegate method, whilst the other threads are locked, waiting for the result. */ 32 | } 33 | 34 | public static void Clear() 35 | { 36 | _cache.Clear(); 37 | } 38 | } 39 | 40 | internal class CacheItem 41 | { 42 | public IDictionary FieldsMap { get; set; } 43 | public IDictionary ConstsMap { get; set; } 44 | public IDictionary EnumsMap { get; set; } 45 | public IEnumerable MethodsList { get; set; } 46 | public IDictionary ParsersMap { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive/Caching/RequestStorage.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | using System; 6 | using System.Collections; 7 | using System.Web; 8 | 9 | namespace ExpressiveAnnotations.MvcUnobtrusive.Caching 10 | { 11 | /// 12 | /// Persists arbitrary key-value pairs for the lifespan of the current HTTP request. 13 | /// 14 | internal static class RequestStorage 15 | { 16 | private static IDictionary Items 17 | { 18 | get 19 | { 20 | if (HttpContext.Current == null) 21 | throw new ApplicationException("HttpContext not available."); 22 | return HttpContext.Current.Items; // location that could be used throughtout the entire HTTP request lifetime 23 | } // (contrary to a session, this one exists only within the period of a single request). 24 | } 25 | 26 | public static T Get(string key) 27 | { 28 | return Items[key] == null 29 | ? default(T) 30 | : (T) Items[key]; 31 | } 32 | 33 | public static void Set(string key, T value) 34 | { 35 | Items[key] = value; 36 | } 37 | 38 | public static void Remove(string key) 39 | { 40 | Items.Remove(key); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive/Helper.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.Diagnostics.CodeAnalysis; 9 | using System.IO; 10 | using System.Linq; 11 | using System.Text; 12 | using Newtonsoft.Json; 13 | 14 | namespace ExpressiveAnnotations.MvcUnobtrusive 15 | { 16 | internal static class Helper 17 | { 18 | public static string GetCoarseType(Type type) 19 | { 20 | Debug.Assert(type != null); 21 | 22 | if (type.IsTimeSpan()) 23 | return "timespan"; 24 | if (type.IsDateTime()) 25 | return "datetime"; 26 | if (type.IsEnum()) 27 | return "enumeration"; 28 | if (type.IsNumeric()) 29 | return "number"; 30 | if (type.IsString()) 31 | return "string"; 32 | if (type.IsBool()) 33 | return "bool"; 34 | if (type.IsGuid()) 35 | return "guid"; 36 | 37 | type = type.IsNullable() ? Nullable.GetUnderlyingType(type) : type; 38 | return type.Name.ToLowerInvariant(); 39 | } 40 | 41 | [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] // code that deals with disposables should be consistent (and classes should be resilient to multiple Dispose() calls) 42 | public static string ToJson(this object data) 43 | { 44 | Debug.Assert(data != null); 45 | 46 | var stringBuilder = new StringBuilder(); 47 | var jsonSerializer = new JsonSerializer(); 48 | using (var stringWriter = new StringWriter(stringBuilder)) 49 | using (var jsonTextWriter = new JsonTextWriter(stringWriter)) 50 | { 51 | jsonSerializer.Serialize(jsonTextWriter, data); 52 | return stringBuilder.ToString(); 53 | } 54 | } 55 | 56 | public static bool SegmentsCollide(this IEnumerable listA, IEnumerablelistB, out string name, out int level) 57 | { 58 | Debug.Assert(listA != null); 59 | Debug.Assert(listB != null); 60 | 61 | name = null; 62 | level = -1; 63 | 64 | var segmentsA = listA.Select(x => x.Split('.')).ToList(); 65 | var segmentsB = listB.Select(x => x.Split('.')).ToList(); 66 | 67 | foreach (var segA in segmentsA) 68 | { 69 | foreach (var segB in segmentsB) 70 | { 71 | var equal = true; 72 | var boundary = new[] {segA.Length, segB.Length}.Min() - 1; 73 | for (var i = 0; i <= boundary; i++) 74 | { 75 | if (segA[i] != segB[i]) 76 | { 77 | equal = false; 78 | break; 79 | } 80 | } 81 | if (equal) 82 | { 83 | name = segA[boundary]; 84 | level = boundary; 85 | return true; 86 | } 87 | } 88 | } 89 | return false; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | using System.Reflection; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.InteropServices; 8 | 9 | // General Information about an assembly is controlled through the following 10 | // set of attributes. Change these attribute values to modify the information 11 | // associated with an assembly. 12 | [assembly: AssemblyTitle("ExpressiveAnnotations.MvcUnobtrusive")] 13 | [assembly: AssemblyCopyright("Copyright © Jarosław Waliszko 2014")] 14 | [assembly: AssemblyProduct("ExpressiveAnnotations.MvcUnobtrusive")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("7a61f32b-50cb-4c55-8122-6e1c8e672c29")] 23 | 24 | #if !SIGNED 25 | [assembly: InternalsVisibleTo("ExpressiveAnnotations.MvcUnobtrusive.Tests")] 26 | #else 27 | [assembly: InternalsVisibleTo("ExpressiveAnnotations.MvcUnobtrusive.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001000ff42e23a8247bbd1c54c6f4428d3e592e505391131b6e28a381dadbb26a88f8407d96afa9993877cd71d3b541470d8ebef5a8dcd780fccf270b1846e14d70b68732f98d8ba9dada92d1f128885fe903011a2185a4be124f5b00618413229f73692638e3c7c22d81cf9365f207a954c6b522183280c07011c325168c148865a4")] 28 | #endif 29 | 30 | // Version information for an assembly consists of the following four values: 31 | // 32 | // Major Version 33 | // Minor Version 34 | // Build Number 35 | // Revision 36 | // 37 | // You can specify all the values or you can default the Build and Revision Numbers 38 | // by using the '*' as shown below: 39 | // [assembly: AssemblyVersion("1.0.*")] 40 | [assembly: AssemblyVersion("2.6.0.0")] 41 | [assembly: AssemblyFileVersion("2.6.0.0")] -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive/Providers/ExpressiveAnnotationsModelValidatorProvider.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Web.Mvc; 9 | using ExpressiveAnnotations.Attributes; 10 | using ExpressiveAnnotations.MvcUnobtrusive.Validators; 11 | 12 | namespace ExpressiveAnnotations.MvcUnobtrusive.Providers 13 | { 14 | /// 15 | /// Data annotations validator provider which automatically registers adapters for expressive validation attributes, i.e. , 16 | /// and additionally respects their processing priorities (if is specified) when validation is executed. 17 | /// 18 | /// 19 | /// Attributes with highest priority (lowest value) will be processed in first place. Attributes without explicitly proivided priorities will be processed later, 20 | /// without any specific order. 21 | /// 22 | public class ExpressiveAnnotationsModelValidatorProvider : DataAnnotationsModelValidatorProvider 23 | { 24 | /// 25 | /// Initializes a new instance of the class. 26 | /// 27 | public ExpressiveAnnotationsModelValidatorProvider() 28 | { 29 | RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequiredIfValidator)); 30 | RegisterAdapter(typeof(AssertThatAttribute), typeof(AssertThatValidator)); 31 | } 32 | 33 | /// 34 | /// Gets a list of validators. 35 | /// 36 | /// The metadata. 37 | /// The context. 38 | /// The list of validation attributes. 39 | /// 40 | /// A list of validators. 41 | /// 42 | protected override IEnumerable GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable attributes) 43 | { 44 | var allAttribs = attributes.ToList(); 45 | var orderedAttribs = allAttribs 46 | .OfType() 47 | .Where(x => x.GetPriority().HasValue) 48 | .OrderBy(x => x.Priority) 49 | .Cast() 50 | .ToList(); 51 | 52 | var setToRemove = new HashSet(orderedAttribs); 53 | allAttribs.RemoveAll(setToRemove.Contains); // allAttribs variable contains only chaotic attribs now 54 | orderedAttribs.AddRange(allAttribs); // chaotic attribs are added after ordered ones 55 | 56 | return base.GetValidators(metadata, context, orderedAttribs); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive/Validators/AssertThatValidator.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | using System.Collections.Generic; 6 | using System.Web.Mvc; 7 | using ExpressiveAnnotations.Attributes; 8 | 9 | namespace ExpressiveAnnotations.MvcUnobtrusive.Validators 10 | { 11 | /// 12 | /// Model validator for . 13 | /// 14 | public class AssertThatValidator : ExpressiveValidator 15 | { 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | /// The model metadata instance. 20 | /// The controller context instance. 21 | /// The expressive assertion attribute instance. 22 | /// 23 | public AssertThatValidator(ModelMetadata metadata, ControllerContext context, AssertThatAttribute attribute) 24 | : base(metadata, context, attribute) 25 | { 26 | } 27 | 28 | /// 29 | /// Retrieves a collection of client validation rules (which are next sent to browsers). 30 | /// 31 | /// 32 | /// A collection of client validation rules. 33 | /// 34 | public override IEnumerable GetClientValidationRules() 35 | { 36 | var rule = GetBasicRule("assertthat"); 37 | yield return rule; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive/Validators/RequiredIfValidator.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel.DataAnnotations; 8 | using System.Web.Mvc; 9 | using ExpressiveAnnotations.Attributes; 10 | 11 | namespace ExpressiveAnnotations.MvcUnobtrusive.Validators 12 | { 13 | /// 14 | /// Model validator for . 15 | /// 16 | public class RequiredIfValidator : ExpressiveValidator 17 | { 18 | /// 19 | /// Initializes a new instance of the class. 20 | /// 21 | /// The model metadata instance. 22 | /// The controller context instance. 23 | /// The expressive requirement attribute instance. 24 | /// 25 | /// 26 | public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute) 27 | : base(metadata, context, attribute) 28 | { 29 | AllowEmpty = attribute.AllowEmptyStrings; 30 | 31 | try 32 | { 33 | var propType = metadata.ModelType; 34 | if (propType.IsNonNullableValueType()) 35 | throw new InvalidOperationException( 36 | $"{nameof(RequiredIfAttribute)} has no effect when applied to a field of non-nullable value type '{propType.FullName}'. Use nullable '{propType.FullName}?' version instead, or switch to {nameof(AssertThatAttribute)} otherwise."); 37 | } 38 | catch (Exception e) 39 | { 40 | throw new ValidationException( 41 | $"{GetType().Name}: validation applied to {metadata.PropertyName} field failed.", e); 42 | } 43 | } 44 | 45 | private bool AllowEmpty { get; set; } 46 | 47 | /// 48 | /// Retrieves a collection of client validation rules (which are next sent to browsers). 49 | /// 50 | /// 51 | /// A collection of client validation rules. 52 | /// 53 | public override IEnumerable GetClientValidationRules() 54 | { 55 | var rule = GetBasicRule("requiredif"); 56 | rule.ValidationParameters.Add("allowempty", AllowEmpty.ToJson()); 57 | yield return rule; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcUnobtrusive/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample.UITests/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample.UITests/AttribsCompileTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using Xunit; 7 | 8 | namespace ExpressiveAnnotations.MvcWebSample.UITests 9 | { 10 | public class AttribsCompileTest 11 | { 12 | private static string GetAssemblyLocation(string assemblyName) // looks inside bin folder of sample project 13 | { 14 | return Path.GetFullPath(Path.Combine( 15 | AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\ExpressiveAnnotations.MvcWebSample\bin\", assemblyName)); 16 | } 17 | 18 | private static Assembly LoadAssembly(object sender, ResolveEventArgs args) 19 | { 20 | var assemblyPath = GetAssemblyLocation($"{new AssemblyName(args.Name).Name}.dll"); 21 | return !File.Exists(assemblyPath) ? null : Assembly.LoadFrom(assemblyPath); 22 | } 23 | 24 | [Fact] 25 | public void expressive_annotations_within_sample_project_compile_with_success() // reveals compile-time errors (no need to wait for application startup) 26 | { 27 | try 28 | { 29 | AppDomain.CurrentDomain.AssemblyResolve += LoadAssembly; 30 | var assemblyPath = GetAssemblyLocation("ExpressiveAnnotations.MvcWebSample.dll"); 31 | var assembly = Assembly.LoadFrom(assemblyPath); 32 | 33 | assembly.GetType("ExpressiveAnnotations.MvcWebSample.Misc.CustomToolchain").GetMethod("Register").Invoke(null, null); // register your custom methods if you defined any 34 | var attribs = assembly.CompileExpressiveAttributes(); 35 | 36 | Assert.Equal(34, attribs.Count()); 37 | } 38 | catch (ReflectionTypeLoadException e) 39 | { 40 | var sb = new StringBuilder(); 41 | sb.AppendLine(e.Message).AppendLine(); 42 | sb.AppendLine("LoaderExceptions:").AppendLine(); 43 | 44 | foreach (var loaderEx in e.LoaderExceptions) 45 | { 46 | sb.AppendLine(loaderEx.Message).AppendLine(); 47 | var fileNotFoundEx = loaderEx as FileNotFoundException; 48 | if (string.IsNullOrEmpty(fileNotFoundEx?.FusionLog)) 49 | continue; 50 | 51 | sb.AppendLine("FusionLog:"); 52 | sb.AppendLine(fileNotFoundEx.FusionLog); 53 | } 54 | 55 | throw new Exception(sb.ToString()); 56 | } 57 | finally 58 | { 59 | AppDomain.CurrentDomain.AssemblyResolve -= LoadAssembly; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample.UITests/BaseTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Drawing.Imaging; 4 | using System.IO; 5 | using System.Runtime.CompilerServices; 6 | using OpenQA.Selenium.Remote; 7 | using Xunit; 8 | 9 | namespace ExpressiveAnnotations.MvcWebSample.UITests 10 | { 11 | public abstract class BaseTest : IDisposable, IClassFixture, IAssemblyFixture 12 | { 13 | private readonly RemoteWebDriver _driver; 14 | 15 | protected BaseTest(DriverFixture classContext, ServerFixture assemblyContext) // called before every test method 16 | { 17 | _driver = classContext.Driver; 18 | Home = new HomePage(classContext.Driver); 19 | Home.Load($"http://localhost:{assemblyContext.Port}/"); 20 | 21 | Debug.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId}: http://localhost:{assemblyContext.Port}/"); 22 | } 23 | 24 | public HomePage Home { get; private set; } 25 | 26 | protected void Watch(Action action, [CallerMemberName] string name = null) // no context available in xUnit to recognize failing tests - 27 | { // - wrapper function used instead (Higher order programming) 28 | try 29 | { 30 | action(); 31 | } 32 | catch 33 | { 34 | TakeScreenshot(name); 35 | throw; 36 | } 37 | } 38 | 39 | protected void TakeScreenshot(string name) 40 | { 41 | var directory = Directory.CreateDirectory("Screenshots"); 42 | 43 | var stamp = DateTime.Now.ToString("HH-mm-ss_fffffff"); 44 | var fullImgPath = $"{directory.Name}/{stamp}.png"; 45 | _driver.GetScreenshot().SaveAsFile(fullImgPath, ImageFormat.Png); 46 | 47 | var fullTxtPath = $"{directory.Name}/toc.txt"; 48 | var entry = $"{stamp} :: {name}\r\n"; 49 | File.AppendAllText(fullTxtPath, entry); 50 | } 51 | 52 | protected virtual void Dispose(bool disposing) 53 | { 54 | if (disposing) 55 | { 56 | if (Home != null) 57 | { 58 | Home.Clean(); 59 | Home = null; 60 | } 61 | } 62 | } 63 | 64 | public void Dispose() // called after every test method 65 | { 66 | Dispose(true); 67 | GC.SuppressFinalize(this); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample.UITests/DriverFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using OpenQA.Selenium.Firefox; 3 | using OpenQA.Selenium.PhantomJS; 4 | using OpenQA.Selenium.Remote; 5 | 6 | namespace ExpressiveAnnotations.MvcWebSample.UITests 7 | { 8 | public class DriverFixture : IDisposable 9 | { 10 | public DriverFixture() // called before every test class 11 | { 12 | //var service = PhantomJSDriverService.CreateDefaultService(); 13 | //service.IgnoreSslErrors = true; 14 | //service.WebSecurity = false; 15 | //Driver = new PhantomJSDriver(service, new PhantomJSOptions(), TimeSpan.FromSeconds(15)); // headless browser testing 16 | 17 | //var service = FirefoxDriverService.CreateDefaultService(); 18 | //service.FirefoxBinaryPath = @"C:\Program Files (x86)\Mozilla Firefox\firefox.exe"; 19 | //service.HideCommandPromptWindow = true; 20 | //service.SuppressInitialDiagnosticInformation = true; 21 | //Driver = new FirefoxDriver(service, new FirefoxOptions(), TimeSpan.FromSeconds(15)); 22 | //Driver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0, 0, 0, 5)); 23 | 24 | var profile = new FirefoxProfile(); 25 | 26 | // try to suppress diagnostic info 27 | profile.SetPreference("webdriver.log.browser.ignore", true); 28 | profile.SetPreference("webdriver.log.driver.ignore", true); 29 | profile.SetPreference("webdriver.log.profiler.ignore", true); 30 | profile.SetPreference("webdriver.log.browser.file", ""); 31 | profile.SetPreference("webdriver.log.driver.file", ""); 32 | profile.SetPreference("webdriver.log.profiler.file", ""); 33 | profile.SetPreference("webdriver.log.init", false); 34 | profile.SetPreference("webdriver.log.driver.level", "OFF"); 35 | 36 | // disable start page 37 | profile.SetPreference("browser.startup.homepage_override.mstone", "ignore"); 38 | profile.SetPreference("startup.homepage_welcome_url.additional", "about:blank"); 39 | Driver = new FirefoxDriver(profile); 40 | } 41 | 42 | public RemoteWebDriver Driver { get; private set; } 43 | 44 | protected virtual void Dispose(bool disposing) 45 | { 46 | if (disposing) 47 | { 48 | if (Driver != null) 49 | { 50 | Driver.Quit(); 51 | Driver = null; 52 | } 53 | } 54 | } 55 | 56 | public void Dispose() // called after every test class 57 | { 58 | Dispose(true); 59 | GC.SuppressFinalize(this); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample.UITests/Helper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using ExpressiveAnnotations.Attributes; 6 | 7 | namespace ExpressiveAnnotations.MvcWebSample.UITests 8 | { 9 | public static class Helper 10 | { 11 | public static IEnumerable CompileExpressiveAttributes(this Assembly assembly) 12 | { 13 | return assembly.GetTypes().SelectMany(CompileExpressiveAttributes); 14 | } 15 | 16 | public static IEnumerable CompileExpressiveAttributes(this Type type) 17 | { 18 | var properties = type.GetProperties() 19 | .Where(p => Attribute.IsDefined(p, typeof(ExpressiveAttribute))); 20 | var attributes = new List(); 21 | 22 | foreach (var prop in properties) 23 | { 24 | var attribs = prop.GetCustomAttributes().ToList(); 25 | attribs.ForEach(x => x.Compile(prop.DeclaringType)); 26 | attributes.AddRange(attribs); 27 | } 28 | return attributes; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample.UITests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using Xunit; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ExpressiveAnnotations.MvcWebSample.UITests")] 9 | [assembly: AssemblyCopyright("Copyright © Jarosław Waliszko 2014")] 10 | [assembly: AssemblyProduct("ExpressiveAnnotations.MvcWebSample.UITests")] 11 | 12 | // Setting ComVisible to false makes the types in this assembly not visible 13 | // to COM components. If you need to access a type in this assembly from 14 | // COM, set the ComVisible attribute to true on that type. 15 | [assembly: ComVisible(false)] 16 | 17 | // The following GUID is for the ID of the typelib if this project is exposed to COM 18 | [assembly: Guid("4af00158-515e-4f7b-9ac7-b30f42f091fa")] 19 | 20 | // Version information for an assembly consists of the following four values: 21 | // 22 | // Major Version 23 | // Minor Version 24 | // Build Number 25 | // Revision 26 | // 27 | // You can specify all the values or you can default the Build and Revision Numbers 28 | // by using the '*' as shown below: 29 | // [assembly: AssemblyVersion("1.0.*")] 30 | [assembly: AssemblyVersion("1.0.0.0")] 31 | [assembly: AssemblyFileVersion("1.0.0.0")] 32 | 33 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerClass, MaxParallelThreads = 3)] // override default setup (e.g. to start both browsers even on a single core) -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample.UITests/ServerFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | 5 | namespace ExpressiveAnnotations.MvcWebSample.UITests 6 | { 7 | public class ServerFixture : IDisposable 8 | { 9 | private Process _iisProcess; 10 | 11 | public ServerFixture() // called before all tests 12 | { 13 | Port = 51622; 14 | var applicationPath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\ExpressiveAnnotations.MvcWebSample")); 15 | 16 | _iisProcess = new Process 17 | { 18 | StartInfo = 19 | { 20 | FileName = @"C:\Program Files\IIS Express\iisexpress.exe", 21 | Arguments = $@"/path:""{applicationPath}"" /port:{Port}" 22 | } 23 | }; 24 | _iisProcess.Start(); 25 | 26 | const string screenshots = "Screenshots"; 27 | if (Directory.Exists(screenshots)) 28 | Directory.Delete(screenshots, true); // remove existing screenshots of previously failing tests, if any 29 | } 30 | 31 | public int Port { get; private set; } 32 | 33 | protected virtual void Dispose(bool disposing) 34 | { 35 | if (disposing) 36 | { 37 | if (_iisProcess != null) 38 | { 39 | try 40 | { 41 | _iisProcess.Kill(); 42 | } 43 | catch 44 | { 45 | // ignored 46 | } 47 | _iisProcess.Dispose(); // MSDN: the Dispose() method calls Close() 48 | _iisProcess = null; 49 | } 50 | } 51 | } 52 | 53 | public void Dispose() // called after all tests 54 | { 55 | Dispose(true); 56 | GC.SuppressFinalize(this); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample.UITests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample.UITests/phantomjs-license.txt: -------------------------------------------------------------------------------- 1 | https://github.com/ariya/phantomjs/blob/master/LICENSE.BSD 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Optimization; 2 | 3 | namespace ExpressiveAnnotations.MvcWebSample 4 | { 5 | public class BundleConfig 6 | { 7 | // For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725 8 | public static void RegisterBundles(BundleCollection bundles) 9 | { 10 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 11 | "~/Scripts/jquery-{version}.js")); 12 | 13 | bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include( 14 | "~/Scripts/jquery-ui-{version}.js")); 15 | 16 | bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 17 | "~/Scripts/jquery.validate*")); 18 | 19 | bundles.Add(new /*Script*/Bundle("~/bundles/expressive").Include( // ScriptBundle uses JsMinify transformation from System.Web.Optimization by default (alternatively, to pick already provided minified version, use Bundle) 20 | "~/Scripts/expressive.annotations*")); 21 | 22 | bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css")); 23 | 24 | bundles.Add(new StyleBundle("~/Content/themes/base/css").Include( 25 | "~/Content/themes/base/jquery.ui.core.css", 26 | "~/Content/themes/base/jquery.ui.resizable.css", 27 | "~/Content/themes/base/jquery.ui.selectable.css", 28 | "~/Content/themes/base/jquery.ui.accordion.css", 29 | "~/Content/themes/base/jquery.ui.autocomplete.css", 30 | "~/Content/themes/base/jquery.ui.button.css", 31 | "~/Content/themes/base/jquery.ui.dialog.css", 32 | "~/Content/themes/base/jquery.ui.slider.css", 33 | "~/Content/themes/base/jquery.ui.tabs.css", 34 | "~/Content/themes/base/jquery.ui.datepicker.css", 35 | "~/Content/themes/base/jquery.ui.progressbar.css", 36 | "~/Content/themes/base/jquery.ui.theme.css")); 37 | 38 | #if !DEBUG 39 | BundleTable.EnableOptimizations = true; // forces bundling in development phase, where it's disabled by default (here, enabled for non-debug configuration only) 40 | // (web.[mode].config transformations are normally executed for deploying/publishing, not just for changing the active configuration in VS) 41 | #endif 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | namespace ExpressiveAnnotations.MvcWebSample 4 | { 5 | public class FilterConfig 6 | { 7 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 8 | { 9 | filters.Add(new HandleErrorAttribute()); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using System.Web.Routing; 3 | 4 | namespace ExpressiveAnnotations.MvcWebSample 5 | { 6 | public class RouteConfig 7 | { 8 | public static void RegisterRoutes(RouteCollection routes) 9 | { 10 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 11 | 12 | routes.MapRoute( 13 | name: "Default", 14 | url: "{controller}/{action}/{id}", 15 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 16 | ); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | 3 | namespace ExpressiveAnnotations.MvcWebSample 4 | { 5 | public static class WebApiConfig 6 | { 7 | public static void Register(HttpConfiguration config) 8 | { 9 | // Web API routes 10 | config.MapHttpAttributeRoutes(); 11 | 12 | config.Routes.MapHttpRoute( 13 | name: "DefaultApi", 14 | routeTemplate: "api/{controller}/{id}", 15 | defaults: new { id = RouteParameter.Optional } 16 | ); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/animated-overlay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/animated-overlay.gif -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.accordion.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Accordion 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/accordion/#theming 10 | */ 11 | .ui-accordion .ui-accordion-header { 12 | display: block; 13 | cursor: pointer; 14 | position: relative; 15 | margin-top: 2px; 16 | padding: .5em .5em .5em .7em; 17 | min-height: 0; /* support: IE7 */ 18 | } 19 | .ui-accordion .ui-accordion-icons { 20 | padding-left: 2.2em; 21 | } 22 | .ui-accordion .ui-accordion-noicons { 23 | padding-left: .7em; 24 | } 25 | .ui-accordion .ui-accordion-icons .ui-accordion-icons { 26 | padding-left: 2.2em; 27 | } 28 | .ui-accordion .ui-accordion-header .ui-accordion-header-icon { 29 | position: absolute; 30 | left: .5em; 31 | top: 50%; 32 | margin-top: -8px; 33 | } 34 | .ui-accordion .ui-accordion-content { 35 | padding: 1em 2.2em; 36 | border-top: 0; 37 | overflow: auto; 38 | } 39 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.all.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI CSS Framework 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/category/theming/ 10 | */ 11 | @import "jquery.ui.base.css"; 12 | @import "jquery.ui.theme.css"; 13 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.autocomplete.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Autocomplete 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/autocomplete/#theming 10 | */ 11 | .ui-autocomplete { 12 | position: absolute; 13 | top: 0; 14 | left: 0; 15 | cursor: default; 16 | } 17 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.base.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI CSS Framework 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/category/theming/ 10 | */ 11 | @import url("jquery.ui.core.css"); 12 | 13 | @import url("jquery.ui.accordion.css"); 14 | @import url("jquery.ui.autocomplete.css"); 15 | @import url("jquery.ui.button.css"); 16 | @import url("jquery.ui.datepicker.css"); 17 | @import url("jquery.ui.dialog.css"); 18 | @import url("jquery.ui.menu.css"); 19 | @import url("jquery.ui.progressbar.css"); 20 | @import url("jquery.ui.resizable.css"); 21 | @import url("jquery.ui.selectable.css"); 22 | @import url("jquery.ui.slider.css"); 23 | @import url("jquery.ui.spinner.css"); 24 | @import url("jquery.ui.tabs.css"); 25 | @import url("jquery.ui.tooltip.css"); 26 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.button.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Button 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/button/#theming 10 | */ 11 | .ui-button { 12 | display: inline-block; 13 | position: relative; 14 | padding: 0; 15 | line-height: normal; 16 | margin-right: .1em; 17 | cursor: pointer; 18 | vertical-align: middle; 19 | text-align: center; 20 | overflow: visible; /* removes extra width in IE */ 21 | } 22 | .ui-button, 23 | .ui-button:link, 24 | .ui-button:visited, 25 | .ui-button:hover, 26 | .ui-button:active { 27 | text-decoration: none; 28 | } 29 | /* to make room for the icon, a width needs to be set here */ 30 | .ui-button-icon-only { 31 | width: 2.2em; 32 | } 33 | /* button elements seem to need a little more width */ 34 | button.ui-button-icon-only { 35 | width: 2.4em; 36 | } 37 | .ui-button-icons-only { 38 | width: 3.4em; 39 | } 40 | button.ui-button-icons-only { 41 | width: 3.7em; 42 | } 43 | 44 | /* button text element */ 45 | .ui-button .ui-button-text { 46 | display: block; 47 | line-height: normal; 48 | } 49 | .ui-button-text-only .ui-button-text { 50 | padding: .4em 1em; 51 | } 52 | .ui-button-icon-only .ui-button-text, 53 | .ui-button-icons-only .ui-button-text { 54 | padding: .4em; 55 | text-indent: -9999999px; 56 | } 57 | .ui-button-text-icon-primary .ui-button-text, 58 | .ui-button-text-icons .ui-button-text { 59 | padding: .4em 1em .4em 2.1em; 60 | } 61 | .ui-button-text-icon-secondary .ui-button-text, 62 | .ui-button-text-icons .ui-button-text { 63 | padding: .4em 2.1em .4em 1em; 64 | } 65 | .ui-button-text-icons .ui-button-text { 66 | padding-left: 2.1em; 67 | padding-right: 2.1em; 68 | } 69 | /* no icon support for input elements, provide padding by default */ 70 | input.ui-button { 71 | padding: .4em 1em; 72 | } 73 | 74 | /* button icon element(s) */ 75 | .ui-button-icon-only .ui-icon, 76 | .ui-button-text-icon-primary .ui-icon, 77 | .ui-button-text-icon-secondary .ui-icon, 78 | .ui-button-text-icons .ui-icon, 79 | .ui-button-icons-only .ui-icon { 80 | position: absolute; 81 | top: 50%; 82 | margin-top: -8px; 83 | } 84 | .ui-button-icon-only .ui-icon { 85 | left: 50%; 86 | margin-left: -8px; 87 | } 88 | .ui-button-text-icon-primary .ui-button-icon-primary, 89 | .ui-button-text-icons .ui-button-icon-primary, 90 | .ui-button-icons-only .ui-button-icon-primary { 91 | left: .5em; 92 | } 93 | .ui-button-text-icon-secondary .ui-button-icon-secondary, 94 | .ui-button-text-icons .ui-button-icon-secondary, 95 | .ui-button-icons-only .ui-button-icon-secondary { 96 | right: .5em; 97 | } 98 | 99 | /* button sets */ 100 | .ui-buttonset { 101 | margin-right: 7px; 102 | } 103 | .ui-buttonset .ui-button { 104 | margin-left: 0; 105 | margin-right: -.3em; 106 | } 107 | 108 | /* workarounds */ 109 | /* reset extra padding in Firefox, see h5bp.com/l */ 110 | input.ui-button::-moz-focus-inner, 111 | button.ui-button::-moz-focus-inner { 112 | border: 0; 113 | padding: 0; 114 | } 115 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.core.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI CSS Framework 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/category/theming/ 10 | */ 11 | 12 | /* Layout helpers 13 | ----------------------------------*/ 14 | .ui-helper-hidden { 15 | display: none; 16 | } 17 | .ui-helper-hidden-accessible { 18 | border: 0; 19 | clip: rect(0 0 0 0); 20 | height: 1px; 21 | margin: -1px; 22 | overflow: hidden; 23 | padding: 0; 24 | position: absolute; 25 | width: 1px; 26 | } 27 | .ui-helper-reset { 28 | margin: 0; 29 | padding: 0; 30 | border: 0; 31 | outline: 0; 32 | line-height: 1.3; 33 | text-decoration: none; 34 | font-size: 100%; 35 | list-style: none; 36 | } 37 | .ui-helper-clearfix:before, 38 | .ui-helper-clearfix:after { 39 | content: ""; 40 | display: table; 41 | border-collapse: collapse; 42 | } 43 | .ui-helper-clearfix:after { 44 | clear: both; 45 | } 46 | .ui-helper-clearfix { 47 | min-height: 0; /* support: IE7 */ 48 | } 49 | .ui-helper-zfix { 50 | width: 100%; 51 | height: 100%; 52 | top: 0; 53 | left: 0; 54 | position: absolute; 55 | opacity: 0; 56 | filter:Alpha(Opacity=0); 57 | } 58 | 59 | .ui-front { 60 | z-index: 100; 61 | } 62 | 63 | 64 | /* Interaction Cues 65 | ----------------------------------*/ 66 | .ui-state-disabled { 67 | cursor: default !important; 68 | } 69 | 70 | 71 | /* Icons 72 | ----------------------------------*/ 73 | 74 | /* states and images */ 75 | .ui-icon { 76 | display: block; 77 | text-indent: -99999px; 78 | overflow: hidden; 79 | background-repeat: no-repeat; 80 | } 81 | 82 | 83 | /* Misc visuals 84 | ----------------------------------*/ 85 | 86 | /* Overlays */ 87 | .ui-widget-overlay { 88 | position: fixed; 89 | top: 0; 90 | left: 0; 91 | width: 100%; 92 | height: 100%; 93 | } 94 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.dialog.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Dialog 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/dialog/#theming 10 | */ 11 | .ui-dialog { 12 | overflow: hidden; 13 | position: absolute; 14 | top: 0; 15 | left: 0; 16 | padding: .2em; 17 | outline: 0; 18 | } 19 | .ui-dialog .ui-dialog-titlebar { 20 | padding: .4em 1em; 21 | position: relative; 22 | } 23 | .ui-dialog .ui-dialog-title { 24 | float: left; 25 | margin: .1em 0; 26 | white-space: nowrap; 27 | width: 90%; 28 | overflow: hidden; 29 | text-overflow: ellipsis; 30 | } 31 | .ui-dialog .ui-dialog-titlebar-close { 32 | position: absolute; 33 | right: .3em; 34 | top: 50%; 35 | width: 20px; 36 | margin: -10px 0 0 0; 37 | padding: 1px; 38 | height: 20px; 39 | } 40 | .ui-dialog .ui-dialog-content { 41 | position: relative; 42 | border: 0; 43 | padding: .5em 1em; 44 | background: none; 45 | overflow: auto; 46 | } 47 | .ui-dialog .ui-dialog-buttonpane { 48 | text-align: left; 49 | border-width: 1px 0 0 0; 50 | background-image: none; 51 | margin-top: .5em; 52 | padding: .3em 1em .5em .4em; 53 | } 54 | .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { 55 | float: right; 56 | } 57 | .ui-dialog .ui-dialog-buttonpane button { 58 | margin: .5em .4em .5em 0; 59 | cursor: pointer; 60 | } 61 | .ui-dialog .ui-resizable-se { 62 | width: 12px; 63 | height: 12px; 64 | right: -5px; 65 | bottom: -5px; 66 | background-position: 16px 16px; 67 | } 68 | .ui-draggable .ui-dialog-titlebar { 69 | cursor: move; 70 | } 71 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.menu.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Menu 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/menu/#theming 10 | */ 11 | .ui-menu { 12 | list-style: none; 13 | padding: 2px; 14 | margin: 0; 15 | display: block; 16 | outline: none; 17 | } 18 | .ui-menu .ui-menu { 19 | margin-top: -3px; 20 | position: absolute; 21 | } 22 | .ui-menu .ui-menu-item { 23 | margin: 0; 24 | padding: 0; 25 | width: 100%; 26 | /* support: IE10, see #8844 */ 27 | list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); 28 | } 29 | .ui-menu .ui-menu-divider { 30 | margin: 5px -2px 5px -2px; 31 | height: 0; 32 | font-size: 0; 33 | line-height: 0; 34 | border-width: 1px 0 0 0; 35 | } 36 | .ui-menu .ui-menu-item a { 37 | text-decoration: none; 38 | display: block; 39 | padding: 2px .4em; 40 | line-height: 1.5; 41 | min-height: 0; /* support: IE7 */ 42 | font-weight: normal; 43 | } 44 | .ui-menu .ui-menu-item a.ui-state-focus, 45 | .ui-menu .ui-menu-item a.ui-state-active { 46 | font-weight: normal; 47 | margin: -1px; 48 | } 49 | 50 | .ui-menu .ui-state-disabled { 51 | font-weight: normal; 52 | margin: .4em 0 .2em; 53 | line-height: 1.5; 54 | } 55 | .ui-menu .ui-state-disabled a { 56 | cursor: default; 57 | } 58 | 59 | /* icon support */ 60 | .ui-menu-icons { 61 | position: relative; 62 | } 63 | .ui-menu-icons .ui-menu-item a { 64 | position: relative; 65 | padding-left: 2em; 66 | } 67 | 68 | /* left-aligned */ 69 | .ui-menu .ui-icon { 70 | position: absolute; 71 | top: .2em; 72 | left: .2em; 73 | } 74 | 75 | /* right-aligned */ 76 | .ui-menu .ui-menu-icon { 77 | position: static; 78 | float: right; 79 | } 80 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.progressbar.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Progressbar 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/progressbar/#theming 10 | */ 11 | .ui-progressbar { 12 | height: 2em; 13 | text-align: left; 14 | overflow: hidden; 15 | } 16 | .ui-progressbar .ui-progressbar-value { 17 | margin: -1px; 18 | height: 100%; 19 | } 20 | .ui-progressbar .ui-progressbar-overlay { 21 | background: url("images/animated-overlay.gif"); 22 | height: 100%; 23 | filter: alpha(opacity=25); 24 | opacity: 0.25; 25 | } 26 | .ui-progressbar-indeterminate .ui-progressbar-value { 27 | background-image: none; 28 | } 29 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.resizable.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Resizable 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | */ 9 | .ui-resizable { 10 | position: relative; 11 | } 12 | .ui-resizable-handle { 13 | position: absolute; 14 | font-size: 0.1px; 15 | display: block; 16 | } 17 | .ui-resizable-disabled .ui-resizable-handle, 18 | .ui-resizable-autohide .ui-resizable-handle { 19 | display: none; 20 | } 21 | .ui-resizable-n { 22 | cursor: n-resize; 23 | height: 7px; 24 | width: 100%; 25 | top: -5px; 26 | left: 0; 27 | } 28 | .ui-resizable-s { 29 | cursor: s-resize; 30 | height: 7px; 31 | width: 100%; 32 | bottom: -5px; 33 | left: 0; 34 | } 35 | .ui-resizable-e { 36 | cursor: e-resize; 37 | width: 7px; 38 | right: -5px; 39 | top: 0; 40 | height: 100%; 41 | } 42 | .ui-resizable-w { 43 | cursor: w-resize; 44 | width: 7px; 45 | left: -5px; 46 | top: 0; 47 | height: 100%; 48 | } 49 | .ui-resizable-se { 50 | cursor: se-resize; 51 | width: 12px; 52 | height: 12px; 53 | right: 1px; 54 | bottom: 1px; 55 | } 56 | .ui-resizable-sw { 57 | cursor: sw-resize; 58 | width: 9px; 59 | height: 9px; 60 | left: -5px; 61 | bottom: -5px; 62 | } 63 | .ui-resizable-nw { 64 | cursor: nw-resize; 65 | width: 9px; 66 | height: 9px; 67 | left: -5px; 68 | top: -5px; 69 | } 70 | .ui-resizable-ne { 71 | cursor: ne-resize; 72 | width: 9px; 73 | height: 9px; 74 | right: -5px; 75 | top: -5px; 76 | } 77 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.selectable.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Selectable 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | */ 9 | .ui-selectable-helper { 10 | position: absolute; 11 | z-index: 100; 12 | border: 1px dotted black; 13 | } 14 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.slider.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Slider 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/slider/#theming 10 | */ 11 | .ui-slider { 12 | position: relative; 13 | text-align: left; 14 | } 15 | .ui-slider .ui-slider-handle { 16 | position: absolute; 17 | z-index: 2; 18 | width: 1.2em; 19 | height: 1.2em; 20 | cursor: default; 21 | } 22 | .ui-slider .ui-slider-range { 23 | position: absolute; 24 | z-index: 1; 25 | font-size: .7em; 26 | display: block; 27 | border: 0; 28 | background-position: 0 0; 29 | } 30 | 31 | /* For IE8 - See #6727 */ 32 | .ui-slider.ui-state-disabled .ui-slider-handle, 33 | .ui-slider.ui-state-disabled .ui-slider-range { 34 | filter: inherit; 35 | } 36 | 37 | .ui-slider-horizontal { 38 | height: .8em; 39 | } 40 | .ui-slider-horizontal .ui-slider-handle { 41 | top: -.3em; 42 | margin-left: -.6em; 43 | } 44 | .ui-slider-horizontal .ui-slider-range { 45 | top: 0; 46 | height: 100%; 47 | } 48 | .ui-slider-horizontal .ui-slider-range-min { 49 | left: 0; 50 | } 51 | .ui-slider-horizontal .ui-slider-range-max { 52 | right: 0; 53 | } 54 | 55 | .ui-slider-vertical { 56 | width: .8em; 57 | height: 100px; 58 | } 59 | .ui-slider-vertical .ui-slider-handle { 60 | left: -.3em; 61 | margin-left: 0; 62 | margin-bottom: -.6em; 63 | } 64 | .ui-slider-vertical .ui-slider-range { 65 | left: 0; 66 | width: 100%; 67 | } 68 | .ui-slider-vertical .ui-slider-range-min { 69 | bottom: 0; 70 | } 71 | .ui-slider-vertical .ui-slider-range-max { 72 | top: 0; 73 | } 74 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.spinner.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Spinner 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/spinner/#theming 10 | */ 11 | .ui-spinner { 12 | position: relative; 13 | display: inline-block; 14 | overflow: hidden; 15 | padding: 0; 16 | vertical-align: middle; 17 | } 18 | .ui-spinner-input { 19 | border: none; 20 | background: none; 21 | color: inherit; 22 | padding: 0; 23 | margin: .2em 0; 24 | vertical-align: middle; 25 | margin-left: .4em; 26 | margin-right: 22px; 27 | } 28 | .ui-spinner-button { 29 | width: 16px; 30 | height: 50%; 31 | font-size: .5em; 32 | padding: 0; 33 | margin: 0; 34 | text-align: center; 35 | position: absolute; 36 | cursor: default; 37 | display: block; 38 | overflow: hidden; 39 | right: 0; 40 | } 41 | /* more specificity required here to override default borders */ 42 | .ui-spinner a.ui-spinner-button { 43 | border-top: none; 44 | border-bottom: none; 45 | border-right: none; 46 | } 47 | /* vertically center icon */ 48 | .ui-spinner .ui-icon { 49 | position: absolute; 50 | margin-top: -8px; 51 | top: 50%; 52 | left: 0; 53 | } 54 | .ui-spinner-up { 55 | top: 0; 56 | } 57 | .ui-spinner-down { 58 | bottom: 0; 59 | } 60 | 61 | /* TR overrides */ 62 | .ui-spinner .ui-icon-triangle-1-s { 63 | /* need to fix icons sprite */ 64 | background-position: -65px -16px; 65 | } 66 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.tabs.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Tabs 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/tabs/#theming 10 | */ 11 | .ui-tabs { 12 | position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ 13 | padding: .2em; 14 | } 15 | .ui-tabs .ui-tabs-nav { 16 | margin: 0; 17 | padding: .2em .2em 0; 18 | } 19 | .ui-tabs .ui-tabs-nav li { 20 | list-style: none; 21 | float: left; 22 | position: relative; 23 | top: 0; 24 | margin: 1px .2em 0 0; 25 | border-bottom-width: 0; 26 | padding: 0; 27 | white-space: nowrap; 28 | } 29 | .ui-tabs .ui-tabs-nav .ui-tabs-anchor { 30 | float: left; 31 | padding: .5em 1em; 32 | text-decoration: none; 33 | } 34 | .ui-tabs .ui-tabs-nav li.ui-tabs-active { 35 | margin-bottom: -1px; 36 | padding-bottom: 1px; 37 | } 38 | .ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor, 39 | .ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor, 40 | .ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor { 41 | cursor: text; 42 | } 43 | .ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor { 44 | cursor: pointer; 45 | } 46 | .ui-tabs .ui-tabs-panel { 47 | display: block; 48 | border-width: 0; 49 | padding: 1em 1.4em; 50 | background: none; 51 | } 52 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/jquery.ui.tooltip.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Tooltip 1.10.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/tooltip/#theming 10 | */ 11 | .ui-tooltip { 12 | padding: 8px; 13 | position: absolute; 14 | z-index: 9999; 15 | max-width: 300px; 16 | -webkit-box-shadow: 0 0 5px #aaa; 17 | box-shadow: 0 0 5px #aaa; 18 | } 19 | body .ui-tooltip { 20 | border-width: 2px; 21 | } 22 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/animated-overlay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/animated-overlay.gif -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.accordion.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin-top:2px;padding:.5em .5em .5em .7em;min-height:0}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-noicons{padding-left:.7em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.autocomplete.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-autocomplete{position:absolute;top:0;left:0;cursor:default} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.button.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.core.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.datepicker.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:700;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.dialog.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:0;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.menu.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-menu{list-style:none;padding:2px;margin:0;display:block;outline:0}.ui-menu .ui-menu{margin-top:-3px;position:absolute}.ui-menu .ui-menu-item{margin:0;padding:0;width:100%;list-style-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}.ui-menu .ui-menu-divider{margin:5px -2px 5px -2px;height:0;font-size:0;line-height:0;border-width:1px 0 0}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.5;min-height:0;font-weight:400}.ui-menu .ui-menu-item a.ui-state-focus,.ui-menu .ui-menu-item a.ui-state-active{font-weight:400;margin:-1px}.ui-menu .ui-state-disabled{font-weight:400;margin:.4em 0 .2em;line-height:1.5}.ui-menu .ui-state-disabled a{cursor:default}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item a{position:relative;padding-left:2em}.ui-menu .ui-icon{position:absolute;top:.2em;left:.2em}.ui-menu .ui-menu-icon{position:static;float:right} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.progressbar.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url(images/animated-overlay.gif);height:100%;filter:alpha(opacity=25);opacity:.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.resizable.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:.1px;display:block}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.selectable.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-selectable-helper{position:absolute;z-index:100;border:1px dotted #000} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.slider.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.spinner.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:0;background:0;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:0;border-bottom:0;border-right:0}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.tabs.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:0} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Content/themes/base/minified/jquery.ui.tooltip.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.4 - 2014-01-17 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px} -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Web.Mvc; 3 | using System.Web.Routing; 4 | using ExpressiveAnnotations.MvcWebSample.Misc; 5 | 6 | namespace ExpressiveAnnotations.MvcWebSample.Controllers 7 | { 8 | public abstract class BaseController : Controller 9 | { 10 | protected override void Initialize(RequestContext requestContext) 11 | { 12 | base.Initialize(requestContext); 13 | 14 | var culture = CultureManager.Instance.Load(HttpContext); 15 | Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = culture; 16 | 17 | var validation = ValidationManager.Instance.Load(HttpContext); 18 | ViewBag.Validation = validation; 19 | 20 | var indication = IndicationsManager.Instance.Load(HttpContext); 21 | ViewBag.Indication = indication; 22 | 23 | var triggers = TriggersManager.Instance.Load(HttpContext); 24 | ViewBag.Triggers = triggers; 25 | 26 | var verbose = VerbosityManager.Instance.Load(HttpContext); 27 | ViewBag.Verbose = verbose; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Controllers/DefaultController.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using System.Web.Http; 3 | using ExpressiveAnnotations.MvcWebSample.Models; 4 | 5 | namespace ExpressiveAnnotations.MvcWebSample.Controllers 6 | { 7 | [System.Web.Http.RoutePrefix("api/Default")] 8 | public class DefaultController : ApiController 9 | { 10 | // POST api/Default/Save 11 | [System.Web.Http.Route("Save")] 12 | public async Task Save(Query model) 13 | { 14 | if (!ModelState.IsValid) 15 | return BadRequest(ModelState); 16 | await Task.Delay(1); 17 | return Ok(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Net.Http; 5 | using System.Net.Http.Formatting; 6 | using System.Threading.Tasks; 7 | using System.Web.Mvc; 8 | using ExpressiveAnnotations.MvcWebSample.Models; 9 | using Newtonsoft.Json; 10 | 11 | namespace ExpressiveAnnotations.MvcWebSample.Controllers 12 | { 13 | public class HomeController : BaseController 14 | { 15 | public ActionResult Index() 16 | { 17 | var model = new Query 18 | { 19 | GoAbroad = true, 20 | Country = "Poland", 21 | NextCountry = "Other", 22 | SportType = "Extreme", 23 | AgreeForContact = false, 24 | SelectedCurrencies = new List {false, false}, 25 | LatestSuggestedReturnDate = DateTime.Today.AddMonths(1) 26 | }; 27 | 28 | Session["Postbacks"] = (int?) TempData["Postbacks"] ?? 0; 29 | ViewBag.Success = TempData["Success"]; 30 | return View("Home", TempData["Query"] as Query ?? model); 31 | } 32 | 33 | [HttpPost] 34 | public async Task Index(Query model) 35 | { 36 | Session["Postbacks"] = (int) Session["Postbacks"] + 1; 37 | if (ModelState.IsValid) 38 | { 39 | var result = await Save(model); 40 | if (!result.IsSuccessStatusCode) 41 | throw new ApplicationException("Unexpected failure in WebAPI pipeline."); 42 | 43 | TempData["Postbacks"] = Session["Postbacks"]; 44 | TempData["Success"] = "[query successfully submitted]"; 45 | TempData["Query"] = model; 46 | return RedirectToAction("Index"); // PRG to avoid subsequent form submission attempts on page refresh (http://en.wikipedia.org/wiki/Post/Redirect/Get) 47 | } 48 | 49 | return View("Home", model); 50 | } 51 | 52 | private async Task Save(Query model) // simulate saving through WebAPI call 53 | { 54 | using (var client = new HttpClient()) 55 | { 56 | Debug.Assert(Request.Url != null); 57 | client.BaseAddress = new Uri($"{Request.Url.Scheme}://{Request.Url.Authority}/"); 58 | var formatter = new JsonMediaTypeFormatter 59 | { 60 | SerializerSettings = {ReferenceLoopHandling = ReferenceLoopHandling.Ignore} 61 | }; 62 | return await client.PostAsync("api/Default/Save", model, formatter); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Controllers/SystemController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using ExpressiveAnnotations.MvcWebSample.Misc; 3 | 4 | namespace ExpressiveAnnotations.MvcWebSample.Controllers 5 | { 6 | public class SystemController : Controller 7 | { 8 | public ActionResult SetCulture(string lang, string returnUrl) 9 | { 10 | CultureManager.Instance.Save(lang, HttpContext); 11 | return Redirect(returnUrl); 12 | } 13 | 14 | public ActionResult SetValidation(string type, string returnUrl) 15 | { 16 | ValidationManager.Instance.Save(type, HttpContext); 17 | return Redirect(returnUrl); 18 | } 19 | 20 | public ActionResult SetIndication(string value, string returnUrl) 21 | { 22 | IndicationsManager.Instance.Save(value, HttpContext); 23 | return Redirect(returnUrl); 24 | } 25 | 26 | [HttpPost] 27 | public JsonResult SetTriggers(string events) 28 | { 29 | TriggersManager.Instance.Save(events, HttpContext); 30 | return Json(new {success = true}); 31 | } 32 | 33 | [HttpPost] 34 | public JsonResult SetVerbosity(bool value) 35 | { 36 | VerbosityManager.Instance.Save(value, HttpContext); 37 | return Json(new {success = true}); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="ExpressiveAnnotations.MvcWebSample.MvcApplication" Language="C#" %> -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Inheritance/CustomAssertThatAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Resources; 5 | using ExpressiveAnnotations.Attributes; 6 | 7 | namespace ExpressiveAnnotations.MvcWebSample.Inheritance 8 | { 9 | public class CustomAssertThatAttribute : ExpressiveAttribute 10 | { 11 | public CustomAssertThatAttribute(string expression) 12 | : base(expression, () => // alternative way of providing dynamic messages resolution - using error message accessor 13 | { 14 | var rm = new ResourceManager(typeof(Resources)); 15 | return rm.GetString("CustomizedAssertThatDefaultError"); 16 | }) 17 | { 18 | } 19 | 20 | public override string FormatErrorMessage(string displayName, string expression, object objectInstance) // use if you need custom formatting 21 | { 22 | var message = base.FormatErrorMessage(displayName, expression, objectInstance); 23 | return $"{message} !?"; 24 | } 25 | 26 | public override string FormatErrorMessage(string displayName, string expression, Type objectType, out IDictionary fieldsMap) // use if you need custom formatting 27 | { 28 | var message = base.FormatErrorMessage(displayName, expression, objectType, out fieldsMap); 29 | return $"{message} ?!"; 30 | } 31 | 32 | protected override ValidationResult IsValidInternal(object value, ValidationContext validationContext) 33 | { 34 | if (value != null) 35 | { 36 | Compile(validationContext.ObjectType); 37 | if (!CachedValidationFuncs[validationContext.ObjectType](validationContext.ObjectInstance)) // check if the assertion condition is not satisfied 38 | return new ValidationResult( // assertion not satisfied => notify 39 | FormatErrorMessage(validationContext.DisplayName, Expression, validationContext.ObjectInstance), 40 | new[] { validationContext.MemberName }); 41 | } 42 | 43 | return ValidationResult.Success; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Inheritance/CustomAssertThatValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Web.Mvc; 3 | using ExpressiveAnnotations.MvcUnobtrusive.Validators; 4 | 5 | namespace ExpressiveAnnotations.MvcWebSample.Inheritance 6 | { 7 | public class CustomAssertThatValidator : ExpressiveValidator 8 | { 9 | public CustomAssertThatValidator(ModelMetadata metadata, ControllerContext context, CustomAssertThatAttribute attribute) 10 | : base(metadata, context, attribute) 11 | { 12 | } 13 | 14 | public override IEnumerable GetClientValidationRules() 15 | { 16 | var rule = GetBasicRule("assertthat"); 17 | yield return rule; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Inheritance/CustomExpressiveAnnotationsModelValidatorProvider.cs: -------------------------------------------------------------------------------- 1 | using ExpressiveAnnotations.MvcUnobtrusive.Providers; 2 | 3 | namespace ExpressiveAnnotations.MvcWebSample.Inheritance 4 | { 5 | public class CustomExpressiveAnnotationsModelValidatorProvider : ExpressiveAnnotationsModelValidatorProvider 6 | { 7 | public CustomExpressiveAnnotationsModelValidatorProvider() 8 | { 9 | RegisterAdapter(typeof(CustomRequiredIfAttribute), typeof(CustomRequiredIfValidator)); 10 | RegisterAdapter(typeof(CustomAssertThatAttribute), typeof(CustomAssertThatValidator)); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Inheritance/CustomRequiredIfAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using ExpressiveAnnotations.Attributes; 3 | 4 | namespace ExpressiveAnnotations.MvcWebSample.Inheritance 5 | { 6 | public class CustomRequiredIfAttribute: ExpressiveAttribute 7 | { 8 | public bool AllowEmptyStrings { get; set; } 9 | 10 | public CustomRequiredIfAttribute(string expression) 11 | : base(expression, "The {0} field is conditionally required.") // this default message will be overriden by resources 12 | { 13 | AllowEmptyStrings = false; 14 | ErrorMessageResourceType = typeof(Resources); 15 | ErrorMessageResourceName = "CustomizedRequiredIfDefaultError"; 16 | } 17 | 18 | protected override ValidationResult IsValidInternal(object value, ValidationContext validationContext) 19 | { 20 | var isEmpty = value is string && string.IsNullOrWhiteSpace((string)value); 21 | if (value == null || (isEmpty && !AllowEmptyStrings)) 22 | { 23 | Compile(validationContext.ObjectType); 24 | if (CachedValidationFuncs[validationContext.ObjectType](validationContext.ObjectInstance)) // check if the requirement condition is satisfied 25 | return new ValidationResult( // requirement confirmed => notify 26 | FormatErrorMessage(validationContext.DisplayName, Expression, validationContext.ObjectInstance), 27 | new[] { validationContext.MemberName }); 28 | } 29 | 30 | return ValidationResult.Success; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Inheritance/CustomRequiredIfValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.IO; 5 | using System.Text; 6 | using System.Web.Mvc; 7 | using ExpressiveAnnotations.MvcUnobtrusive.Validators; 8 | using Newtonsoft.Json; 9 | 10 | namespace ExpressiveAnnotations.MvcWebSample.Inheritance 11 | { 12 | public class CustomRequiredIfValidator : ExpressiveValidator 13 | { 14 | public CustomRequiredIfValidator(ModelMetadata metadata, ControllerContext context, CustomRequiredIfAttribute attribute) 15 | : base(metadata, context, attribute) 16 | { 17 | AllowEmpty = attribute.AllowEmptyStrings; 18 | } 19 | 20 | private bool AllowEmpty { get; set; } 21 | 22 | public override IEnumerable GetClientValidationRules() 23 | { 24 | var rule = GetBasicRule("requiredif"); 25 | rule.ValidationParameters.Add("allowempty", ToJson(AllowEmpty)); 26 | yield return rule; 27 | } 28 | 29 | [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] // code that deals with disposables should be consistent (and classes should be resilient to multiple Dispose() calls) 30 | private static string ToJson(object data) 31 | { 32 | Debug.Assert(data != null); 33 | 34 | var stringBuilder = new StringBuilder(); 35 | var jsonSerializer = new JsonSerializer(); 36 | using (var stringWriter = new StringWriter(stringBuilder)) 37 | using (var jsonTextWriter = new JsonTextWriter(stringWriter)) 38 | { 39 | jsonSerializer.Serialize(jsonTextWriter, data); 40 | return stringBuilder.ToString(); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Misc/CultureManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Web; 4 | 5 | namespace ExpressiveAnnotations.MvcWebSample.Misc 6 | { 7 | public class CultureManager 8 | { 9 | static CultureManager() 10 | { 11 | Instance = new CultureManager(); 12 | } 13 | 14 | private CultureManager() 15 | { 16 | } 17 | 18 | public static CultureManager Instance { get; } 19 | 20 | public void Save(string lang, HttpContextBase httpContext) 21 | { 22 | var culture = CultureInfo.CreateSpecificCulture(lang); 23 | SetValueToCookie(culture, httpContext); 24 | } 25 | 26 | public CultureInfo Load(HttpContextBase httpContext) 27 | { 28 | var culture = GetValueFromCookie(httpContext); 29 | if (culture != null) 30 | return culture; 31 | 32 | culture = CultureInfo.CreateSpecificCulture("en"); // force default culture to be "en" 33 | SetValueToCookie(culture, httpContext); 34 | return culture; 35 | } 36 | 37 | private CultureInfo GetValueFromCookie(HttpContextBase httpContext) 38 | { 39 | var cookie = httpContext.Request.Cookies.Get("expressiv.mvcwebsample.culture"); 40 | return cookie != null ? CultureInfo.CreateSpecificCulture(cookie.Value) : null; 41 | } 42 | 43 | private void SetValueToCookie(CultureInfo culture, HttpContextBase httpContext) 44 | { 45 | var cookie = new HttpCookie("expressiv.mvcwebsample.culture", culture.Name) { Expires = DateTime.Now.AddMonths(1) }; 46 | httpContext.Response.SetCookie(cookie); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Misc/CustomToolchain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using ExpressiveAnnotations.Functions; 6 | 7 | namespace ExpressiveAnnotations.MvcWebSample.Misc 8 | { 9 | //public class CustomFunctionsProvider : IFunctionsProvider 10 | //{ 11 | // public IDictionary> GetFunctions() 12 | // { 13 | // return new Dictionary> 14 | // { 15 | // {"IntArrayLength", new LambdaExpression[] {(Expression>) (array => array.Length)}}, 16 | // {"StringArrayLength", new LambdaExpression[] {(Expression>) (array => array.Length)}}, 17 | // {"IntArrayContains", new LambdaExpression[] {(Expression>) ((value, array) => value != null && array.Contains((int)value))}} 18 | // }; 19 | // } 20 | //} 21 | 22 | public static class CustomToolchain 23 | { 24 | public static void Register() 25 | { 26 | // Toolchain.Instance.Recharge(new CustomFunctionsProvider()); // load complately new set of functions... 27 | // ...or simply add some new ones to existing toolchain: 28 | Toolchain.Instance.AddFunction("IntArrayLength", array => array.Length); 29 | Toolchain.Instance.AddFunction("StringArrayLength", array => array.Length); 30 | Toolchain.Instance.AddFunction("IntArrayContains", (value, array) => value != null && array.Contains((int)value)); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Misc/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Web.Mvc; 7 | using System.Web.Mvc.Html; 8 | using System.Reflection; 9 | 10 | namespace ExpressiveAnnotations.MvcWebSample.Misc 11 | { 12 | public static class Extensions 13 | { 14 | public static MvcHtmlString EnumDropDownListFor(this HtmlHelper htmlHelper, Expression> expression, bool numericValues = true) 15 | { 16 | return EnumDropDownListFor(htmlHelper, expression, numericValues, null); 17 | } 18 | 19 | public static MvcHtmlString EnumDropDownListFor(this HtmlHelper htmlHelper, Expression> expression, bool numericValues, object htmlAttributes) 20 | { 21 | var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); 22 | var type = Nullable.GetUnderlyingType(metadata.ModelType) ?? metadata.ModelType; 23 | if (!type.IsEnum) 24 | throw new ArgumentException 25 | ("Given parameter expression has to indicate enum type.", nameof(expression)); 26 | var values = Enum.GetValues(type).Cast(); 27 | 28 | var items = values.Select(value => new SelectListItem 29 | { 30 | Text = GetEnumDisplayText(value), 31 | Value = numericValues 32 | ? string.Format(CultureInfo.InvariantCulture, $"{Convert.ChangeType(value, value.GetType().GetEnumUnderlyingType())}") 33 | : string.Format(CultureInfo.InvariantCulture, $"{value}"), 34 | Selected = value.Equals(metadata.Model) 35 | }); 36 | 37 | if (metadata.IsNullableValueType) // if the enum is nullable, add an empty item to the collection 38 | items = new[] {new SelectListItem()}.Concat(items); 39 | 40 | return htmlHelper.DropDownListFor(expression, items, htmlAttributes); 41 | } 42 | 43 | private static string GetEnumDisplayText(TEnum value) 44 | { 45 | var field = value.GetType().GetField(value.ToString()); 46 | var attrib = field.GetCustomAttributes().FirstOrDefault(); 47 | return attrib != null ? attrib.GetName() : value.ToString(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Misc/IndicationsManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web; 3 | 4 | namespace ExpressiveAnnotations.MvcWebSample.Misc 5 | { 6 | public class IndicationsManager 7 | { 8 | static IndicationsManager() 9 | { 10 | Instance = new IndicationsManager(); 11 | } 12 | 13 | private IndicationsManager() 14 | { 15 | } 16 | 17 | public static IndicationsManager Instance { get; } 18 | 19 | public void Save(string value, HttpContextBase httpContext) 20 | { 21 | SetValueToCookie(value, httpContext); 22 | } 23 | 24 | public string Load(HttpContextBase httpContext) 25 | { 26 | var indication = GetValueFromCookie(httpContext); 27 | if (indication != null) 28 | return indication; 29 | 30 | indication = "asterisks"; // asterisks by default 31 | SetValueToCookie(indication, httpContext); 32 | return indication; 33 | } 34 | 35 | private string GetValueFromCookie(HttpContextBase httpContext) 36 | { 37 | var cookie = httpContext.Request.Cookies.Get("expressiv.mvcwebsample.indication"); 38 | return cookie?.Value; 39 | } 40 | 41 | private void SetValueToCookie(string value, HttpContextBase httpContext) 42 | { 43 | var cookie = new HttpCookie("expressiv.mvcwebsample.indication", value) { Expires = DateTime.Now.AddMonths(1) }; 44 | httpContext.Response.SetCookie(cookie); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Misc/TriggersManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web; 3 | 4 | namespace ExpressiveAnnotations.MvcWebSample.Misc 5 | { 6 | public class TriggersManager 7 | { 8 | static TriggersManager() 9 | { 10 | Instance = new TriggersManager(); 11 | } 12 | 13 | private TriggersManager() 14 | { 15 | } 16 | 17 | public static TriggersManager Instance { get; } 18 | 19 | public void Save(string events, HttpContextBase httpContext) 20 | { 21 | SetValueToCookie(events, httpContext); 22 | } 23 | 24 | public string Load(HttpContextBase httpContext) 25 | { 26 | var events = GetValueFromCookie(httpContext); 27 | if (events != null) 28 | return events; 29 | 30 | events = "change afterpaste keyup"; 31 | SetValueToCookie(events, httpContext); 32 | return events; 33 | } 34 | 35 | private string GetValueFromCookie(HttpContextBase httpContext) 36 | { 37 | var cookie = httpContext.Request.Cookies.Get("expressiv.mvcwebsample.triggers"); 38 | return cookie?.Value; 39 | } 40 | 41 | private void SetValueToCookie(string events, HttpContextBase httpContext) 42 | { 43 | var cookie = new HttpCookie("expressiv.mvcwebsample.triggers", events) {Expires = DateTime.Now.AddMonths(1)}; 44 | httpContext.Response.SetCookie(cookie); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Misc/ValidationManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web; 3 | 4 | namespace ExpressiveAnnotations.MvcWebSample.Misc 5 | { 6 | public class ValidationManager 7 | { 8 | static ValidationManager() 9 | { 10 | Instance = new ValidationManager(); 11 | } 12 | 13 | private ValidationManager() 14 | { 15 | } 16 | 17 | public static ValidationManager Instance { get; } 18 | 19 | public void Save(string type, HttpContextBase httpContext) 20 | { 21 | SetValueToCookie(type, httpContext); 22 | } 23 | 24 | public string Load(HttpContextBase httpContext) 25 | { 26 | var type = GetValueFromCookie(httpContext); 27 | if (type != null) 28 | return type; 29 | 30 | type = "client"; 31 | SetValueToCookie(type, httpContext); 32 | return type; 33 | } 34 | 35 | private string GetValueFromCookie(HttpContextBase httpContext) 36 | { 37 | var cookie = httpContext.Request.Cookies.Get("expressiv.mvcwebsample.validation"); 38 | return cookie?.Value; 39 | } 40 | 41 | private void SetValueToCookie(string type, HttpContextBase httpContext) 42 | { 43 | var cookie = new HttpCookie("expressiv.mvcwebsample.validation", type) {Expires = DateTime.Now.AddMonths(1)}; 44 | httpContext.Response.SetCookie(cookie); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Misc/VerbosityManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web; 3 | 4 | namespace ExpressiveAnnotations.MvcWebSample.Misc 5 | { 6 | public class VerbosityManager 7 | { 8 | static VerbosityManager() 9 | { 10 | Instance = new VerbosityManager(); 11 | } 12 | 13 | private VerbosityManager() 14 | { 15 | } 16 | 17 | public static VerbosityManager Instance { get; } 18 | 19 | public void Save(bool value, HttpContextBase httpContext) 20 | { 21 | SetValueToCookie(value, httpContext); 22 | } 23 | 24 | public bool Load(HttpContextBase httpContext) 25 | { 26 | var verbose = GetValueFromCookie(httpContext); 27 | if (verbose != null) 28 | return verbose.Value; 29 | 30 | verbose = IsDebug(); 31 | SetValueToCookie(verbose.Value, httpContext); 32 | return verbose.Value; 33 | } 34 | 35 | private bool? GetValueFromCookie(HttpContextBase httpContext) 36 | { 37 | var cookie = httpContext.Request.Cookies.Get("expressiv.mvcwebsample.verbosity"); 38 | if (cookie == null) 39 | return null; 40 | 41 | bool result; 42 | bool.TryParse(cookie.Value, out result); 43 | return result; 44 | } 45 | 46 | private void SetValueToCookie(bool value, HttpContextBase httpContext) 47 | { 48 | var cookie = new HttpCookie("expressiv.mvcwebsample.verbosity", value.ToString().ToLowerInvariant()) { Expires = DateTime.Now.AddMonths(1) }; 49 | httpContext.Response.SetCookie(cookie); 50 | } 51 | 52 | private static bool IsDebug() 53 | { 54 | #if DEBUG 55 | return true; 56 | #else 57 | return false; 58 | #endif 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Models/Address.cs: -------------------------------------------------------------------------------- 1 | using ExpressiveAnnotations.Attributes; 2 | 3 | namespace ExpressiveAnnotations.MvcWebSample.Models 4 | { 5 | public class Address 6 | { 7 | public string Type { get; set; } 8 | 9 | [AssertThat("StartsWith(Details, StreetPrefix)", Priority = 1, 10 | ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = nameof(Resources.AddressDetailsFormatInvalid))] 11 | [AssertThat("Length(Trim(Details)) > Length(StreetPrefix) + 1", Priority = 2, 12 | ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = nameof(Resources.AddressDetailsTooShort))] 13 | public string Details { get; set; } 14 | 15 | public string StreetPrefix => Resources.StreetPrefix; 16 | } 17 | } -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Models/Contact.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using ExpressiveAnnotations.Attributes; 4 | 5 | namespace ExpressiveAnnotations.MvcWebSample.Models 6 | { 7 | public class Contact 8 | { 9 | public Contact() 10 | { 11 | Addresses = new List
12 | { 13 | new Address {Type = Resources.HomeAddress}, 14 | new Address {Type = Resources.OfficeAddress} 15 | }; 16 | } 17 | 18 | public Query Parent { get; set; } 19 | 20 | [RequiredIf("Parent.GoAbroad && Phone == null", // reference to Parent.GoAbroad must by explicitly defined in the view 21 | ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = nameof(Resources.EmailOrPhoneRequired))] 22 | [AssertThat("IsEmail(Email)", 23 | ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = nameof(Resources.EmailFormatInvalid))] 24 | [Display(ResourceType = typeof(Resources), Name = nameof(Resources.Email))] 25 | public string Email { get; set; } 26 | 27 | [RequiredIf("Parent.GoAbroad && Email == null", 28 | ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = nameof(Resources.EmailOrPhoneRequired))] 29 | //[AssertThat("IsPhone(Phone)")] 30 | [AssertThat(@"IsRegexMatch(Phone, '^\\d+$')", // regex pattern escaped despite verbatim string - it's because our expressive language parser 31 | // verbatim syntax should be perfectly valid with that one JavaScript accepts 32 | ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = nameof(Resources.DigitsOnlyAccepted), Priority = 1)] 33 | [AssertThat("Length(Phone) > 8 && Length(Phone) < 16", 34 | ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = nameof(Resources.RangeViolated), Priority = 2)] 35 | [Display(ResourceType = typeof(Resources), Name = nameof(Resources.Phone))] 36 | public string Phone { get; set; } 37 | 38 | public List
Addresses { get; set; } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("ExpressiveAnnotations.MvcWebSample")] 8 | [assembly: AssemblyCopyright("Copyright © Jarosław Waliszko 2014")] 9 | [assembly: AssemblyProduct("ExpressiveAnnotations.MvcWebSample")] 10 | 11 | // Setting ComVisible to false makes the types in this assembly not visible 12 | // to COM components. If you need to access a type in this assembly from 13 | // COM, set the ComVisible attribute to true on that type. 14 | [assembly: ComVisible(false)] 15 | 16 | // The following GUID is for the ID of the typelib if this project is exposed to COM 17 | [assembly: Guid("8b961e76-9cf3-46bf-bbb0-53e72c1a7e74")] 18 | 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Revision and Build Numbers 27 | // by using the '*' as shown below: 28 | [assembly: AssemblyVersion("1.0.0.0")] 29 | [assembly: AssemblyFileVersion("1.0.0.0")] 30 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/README.md: -------------------------------------------------------------------------------- 1 | #### Sample application illustrating model validation in ASP.NET MVC using conditional data annotations (http://expressiveannotations.net/). 2 | --- 3 | ![screenshot](screenshot.png "screenshot") 4 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Views/Home/EditorTemplates/Address.cshtml: -------------------------------------------------------------------------------- 1 | @model ExpressiveAnnotations.MvcWebSample.Models.Address 2 | 3 |
 4 | [AssertThat("StartsWith(Details, StreetPrefix)")]
 5 | [AssertThat("Length(Trim(Details)) > Length(StreetPrefix) + 1")]
 6 | 
7 |
[show attribute]
8 |
9 | @Html.Label(Model.Type, new { @class = "inline prefix" }) 10 | @Html.TextBoxFor(model => model.Details) 11 | @Html.ValidationMessageFor(model => model.Details) 12 | 13 | @*hidden backing fields*@ 14 | @Html.HiddenFor(model => model.StreetPrefix) 15 | @Html.HiddenFor(model => model.Type) 16 |
17 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Views/Home/EditorTemplates/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @model ExpressiveAnnotations.MvcWebSample.Models.Contact 2 | 3 | @{ 4 | var client = "client".Equals(ViewBag.Validation); 5 | } 6 | 7 |
    8 |
  1. 9 |
    10 | [RequiredIf("Parent.GoAbroad && Phone == null")]
    11 | [AssertThat("IsEmail(Email)")]
    12 | 
    13 |
    [show attribute]
    14 |
    15 | @Html.LabelFor(model => model.Email, new { @class = "inline prefix" }) 16 | @Html.TextBoxFor(model => model.Email) 17 | @Html.ValidationMessageFor(model => model.Email) 18 |
    19 |
  2. 20 |
  3. 21 |
    22 | [RequiredIf("Parent.GoAbroad && Email == null")]
    23 | [AssertThat(@@"IsRegexMatch(Phone, '^\\d+$'), Priority = 1")]
    24 | [AssertThat("Length(Phone) > 8 && Length(Phone) < 16", Priority = 2)]
    25 | 
    26 |
    [show attribute]
    27 |
    28 | @Html.LabelFor(model => model.Phone, new { @class = "inline prefix" }) 29 | @Html.TextBoxFor(model => model.Phone) 30 | @Html.ValidationMessageFor(model => model.Phone) 31 |
    32 |
  4. 33 | 34 | @for (var i = 0; i < Model.Addresses.Count; i++) 35 | { 36 |
  5. 37 | @Html.EditorFor(model => Model.Addresses[i]) 38 |
  6. 39 | } 40 | 41 |
42 | 43 | @if (client) 44 | { 45 | @Html.HiddenFor(m => m.Parent.GoAbroad) @*hidden mock of GoAbroad field from Parent context*@ 46 | } 47 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Views/Shared/DisplayTemplates/ISO8601Date.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Globalization 2 | @model DateTime 3 | 4 | @Html.Hidden("", Model.ToString("o", CultureInfo.InvariantCulture)) @*From MSDN: The "o" standard format specifier represents a custom date and time format 5 | string using a pattern that preserves time zone information and emits a result string 6 | that complies with ISO 8601*@ 7 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Views/Shared/DisplayTemplates/IntArray.cshtml: -------------------------------------------------------------------------------- 1 | @using Newtonsoft.Json 2 | @model int[] 3 | 4 | @Html.Hidden("", JsonConvert.SerializeObject(Model)) -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Views/Shared/_Culture.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Threading 2 | 3 | @{ 4 | var plLabel = "pl".Equals(Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName) ? "[polski]" : "polski"; 5 | var enLabel = "en".Equals(Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName) ? "[english]" : "english"; 6 | } 7 |
8 | change language to test localized information: 9 | @plLabel 10 | @enLabel 11 |
12 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Views/Shared/_EAOptions.cshtml: -------------------------------------------------------------------------------- 1 | @if ("client".Equals(ViewBag.Validation)) 2 | { 3 |
4 | set dependency validation triggers: 5 | change 6 | paste 7 | keyup 8 |
9 | 13 | } 14 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Views/Shared/_Indications.cshtml: -------------------------------------------------------------------------------- 1 | @if ("client".Equals(ViewBag.Validation)) 2 | { 3 | var asterisksLabel = "asterisks".Equals(ViewBag.Indication) ? "[asterisks]" : "asterisks"; 4 | var asterisksWithHidingLabel = "asterisks-with-hiding".Equals(ViewBag.Indication) ? "[asterisks-with-hiding]" : "asterisks-with-hiding"; 5 | var noAnnotatingLabel = "no-annotating".Equals(ViewBag.Indication) ? "[no-annotating]" : "no-annotating"; 6 | 7 |
8 | annotate required fields using: 9 | @asterisksLabel 10 | @asterisksWithHidingLabel 11 | @noAnnotatingLabel 12 |
13 | } 14 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Threading 2 | 3 | 4 | @{ 5 | var lang = Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName; 6 | var browser = Request.Browser.Browser.ToLowerInvariant(); 7 | } 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | @*counter for ui tests*@ 16 | 17 | 18 | ExpressiveAnnotations 19 | @Styles.Render("~/Content/css") 20 | @Styles.Render("~/Content/themes/base/css") 21 | 22 | 23 |
24 |
25 | @RenderBody() 26 |
27 |
28 | 29 | @Scripts.Render("~/bundles/jquery") 30 | @Scripts.Render("~/bundles/jqueryui") 31 | @RenderSection("scripts", required: false) 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Views/Shared/_Validation.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | var clientLabel = "client".Equals(ViewBag.Validation) ? "[client]" : "client"; 3 | var serverLabel = "server".Equals(ViewBag.Validation) ? "[server]" : "server"; 4 | } 5 |
6 | disable or enable client-side validation: 7 | @clientLabel 8 | @serverLabel 9 |
10 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Views/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 40 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Web.Debug.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Web.Release.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/favicon.ico -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvcWebSample/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvcWebSample/screenshot.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Misc/FormatStringConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace ExpressiveAnnotations.MvvmDesktopSample.Misc 6 | { 7 | public class FormatStringConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | var stringValue = (value as DateTime?)?.ToShortDateString() ?? value.ToString(); 12 | 13 | if (parameter == null) 14 | return stringValue; 15 | 16 | var formatterString = parameter.ToString(); 17 | return string.IsNullOrEmpty(formatterString) 18 | ? stringValue 19 | : string.Format(culture, formatterString, stringValue); 20 | } 21 | 22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 23 | { 24 | throw new NotImplementedException(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Misc/NullableEnumerationExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Windows.Markup; 6 | 7 | namespace ExpressiveAnnotations.MvvmDesktopSample.Misc 8 | { 9 | public class NullableEnumerationExtension : MarkupExtension 10 | { 11 | private Type _enumType; 12 | 13 | public NullableEnumerationExtension(Type enumType) 14 | { 15 | EnumType = enumType ?? throw new ArgumentNullException(nameof(enumType)); 16 | } 17 | 18 | private Type EnumType 19 | { 20 | get => _enumType; 21 | set 22 | { 23 | if (_enumType == value) 24 | return; 25 | 26 | var enumType = value; 27 | if (enumType.IsEnum == false) 28 | throw new ArgumentException("Type must be an Enum."); 29 | 30 | _enumType = enumType; 31 | } 32 | } 33 | 34 | public override object ProvideValue(IServiceProvider serviceProvider) 35 | { 36 | var enumValues = Enum.GetValues(EnumType); 37 | var enumItems = enumValues 38 | .Cast() 39 | .Select(enumValue => new EnumItem 40 | { 41 | Value = enumValue, 42 | Name = GetDisplayName(enumValue) 43 | }) 44 | .ToArray(); 45 | 46 | enumItems = new[] {new EnumItem {Value = null, Name = string.Empty}}.Concat(enumItems).ToArray(); 47 | return enumItems; 48 | } 49 | 50 | private string GetDisplayName(object enumValue) 51 | { 52 | var displayAttrib = EnumType 53 | .GetField(enumValue.ToString()) 54 | .GetCustomAttributes() 55 | .FirstOrDefault(); 56 | 57 | return displayAttrib != null ? displayAttrib.Name : enumValue.ToString(); 58 | } 59 | 60 | public class EnumItem 61 | { 62 | public string Name { get; set; } 63 | public object Value { get; set; } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Misc/RadioButtonCheckedConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace ExpressiveAnnotations.MvvmDesktopSample.Misc 6 | { 7 | public class RadioButtonCheckedConverter : IValueConverter // http://stackoverflow.com/q/397556/270315 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | return value.Equals(parameter); 12 | } 13 | 14 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | return value.Equals(true) ? parameter : Binding.DoNothing; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Misc/StringToBooleanConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace ExpressiveAnnotations.MvvmDesktopSample.Misc 6 | { 7 | public class StringToBooleanConverter : IValueConverter // http://stackoverflow.com/q/18449890/270315 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | return (bool) value == bool.Parse((string) parameter); 12 | } 13 | 14 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | return bool.Parse((string) parameter); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Models/AddressVM.cs: -------------------------------------------------------------------------------- 1 | using ExpressiveAnnotations.Attributes; 2 | 3 | namespace ExpressiveAnnotations.MvvmDesktopSample.Models 4 | { 5 | public class AddressVm : ExpressiveVM 6 | { 7 | private string _details; 8 | 9 | public string Type { get; set; } 10 | 11 | [AssertThat("StartsWith(Details, 'Street')")] 12 | public string Details 13 | { 14 | get => _details; 15 | set 16 | { 17 | _details = value; 18 | OnPropertyChanged(); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Models/ContactVM.cs: -------------------------------------------------------------------------------- 1 | using ExpressiveAnnotations.Attributes; 2 | 3 | namespace ExpressiveAnnotations.MvvmDesktopSample.Models 4 | { 5 | public class ContactVM : ExpressiveVM 6 | { 7 | private string _email; 8 | private string _phone; 9 | 10 | public ContactVM() 11 | { 12 | Addresses = new[] { new AddressVm { Type = "Home address" }, new AddressVm { Type = "Office address" } }; 13 | } 14 | 15 | [RequiredIf("Phone == null")] 16 | [AssertThat("IsEmail(Email)")] 17 | public string Email 18 | { 19 | get => _email; 20 | set 21 | { 22 | _email = value; 23 | OnPropertyChanged(); 24 | } 25 | } 26 | 27 | [RequiredIf("Email == null")] 28 | [AssertThat(@"IsRegexMatch(Phone, '^\\d+$')")] 29 | [AssertThat("Length(Phone) > 8 && Length(Phone) < 16")] 30 | public string Phone 31 | { 32 | get => _phone; 33 | set 34 | { 35 | _phone = value; 36 | OnPropertyChanged(); 37 | } 38 | } 39 | 40 | public AddressVm[] Addresses { get; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Models/ExpressiveVM.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Runtime.CompilerServices; 8 | using System.Windows; 9 | 10 | namespace ExpressiveAnnotations.MvvmDesktopSample.Models 11 | { 12 | public abstract class ExpressiveVM : INotifyPropertyChanged, IDataErrorInfo 13 | { 14 | static ExpressiveVM() 15 | { 16 | ValidationErrorsMap = new Dictionary>(); 17 | AnnotatedPropertiesMap = new Dictionary>(); 18 | } 19 | 20 | protected ExpressiveVM() 21 | { 22 | AnnotatedPropertiesMap[this] = GetType().GetProperties() 23 | .Where(p => p.GetCustomAttributes().Any(a => a is ValidationAttribute)) 24 | .ToList(); 25 | } 26 | 27 | public static event EventHandler ValidationStateChanged; 28 | public static Dictionary> ValidationErrorsMap { get; } 29 | public static Dictionary> AnnotatedPropertiesMap { get; } 30 | 31 | public event PropertyChangedEventHandler PropertyChanged; 32 | 33 | public string this[string propertyName] 34 | { 35 | get 36 | { 37 | var value = GetType().GetProperty(propertyName).GetValue(this); 38 | var context = new ValidationContext(this) {MemberName = propertyName}; 39 | var results = new List(); 40 | 41 | try 42 | { 43 | Validator.TryValidateProperty(value, context, results); 44 | } 45 | catch (Exception e) 46 | { 47 | MessageBox.Show(e.InnerException.Message, e.Message, MessageBoxButton.OK, MessageBoxImage.Error); 48 | throw; 49 | } 50 | 51 | var errors = results.Select(x => x.ErrorMessage).ToList(); 52 | UpdateValidationErrors(propertyName, this, errors); 53 | return string.Join(Environment.NewLine, errors); 54 | } 55 | } 56 | 57 | public string Error => null; 58 | 59 | protected void OnValidationStateChanged() 60 | { 61 | ValidationStateChanged?.Invoke(this, new EventArgs()); 62 | } 63 | 64 | protected void OnPropertyChanged([CallerMemberName] string propertyName = null) 65 | { 66 | Revalidate(); 67 | } 68 | 69 | private void Revalidate() 70 | { 71 | foreach (var kvp in AnnotatedPropertiesMap) 72 | { 73 | var context = kvp.Key; 74 | var properties = kvp.Value; 75 | foreach (var prop in properties) 76 | { 77 | if (context.PropertyChanged != null) 78 | PropertyChanged(context, new PropertyChangedEventArgs(prop.Name)); 79 | } 80 | } 81 | } 82 | 83 | private void UpdateValidationErrors(string propertyName, object context, IEnumerable errors) 84 | { 85 | errors = errors.ToList(); 86 | var fullName = $"{GetType().FullName}.{propertyName}.{RuntimeHelpers.GetHashCode(context)}"; 87 | var before = ValidationErrorsMap.Count(x => x.Value.Any()); 88 | ValidationErrorsMap[fullName] = errors; 89 | var after = ValidationErrorsMap.Count(x => x.Value.Any()); 90 | 91 | if (before != after) 92 | OnValidationStateChanged(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Models/MainWindowVM.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | namespace ExpressiveAnnotations.MvvmDesktopSample.Models 8 | { 9 | public class MainWindowVM : INotifyPropertyChanged 10 | { 11 | public MainWindowVM() 12 | { 13 | ExpressiveVM.ValidationStateChanged += (sender, args) => OnPropertyChanged(() => Progress); 14 | Query = new QueryVM(); 15 | } 16 | 17 | public event PropertyChangedEventHandler PropertyChanged; 18 | public QueryVM Query { get; set; } 19 | 20 | public double Progress 21 | { 22 | get 23 | { 24 | var fieldsCount = ExpressiveVM.AnnotatedPropertiesMap.SelectMany(x => x.Value).Count(); 25 | var errorsCount = ExpressiveVM.ValidationErrorsMap.Count(x => x.Value.Any()); 26 | var passedCount = fieldsCount - errorsCount; 27 | return (double) passedCount/fieldsCount; 28 | } 29 | } 30 | 31 | protected void OnPropertyChanged(Expression> propertyExpression) 32 | { 33 | if (PropertyChanged != null) 34 | { 35 | var propertyName = GetPropertyName(propertyExpression); 36 | PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 37 | } 38 | } 39 | 40 | private string GetPropertyName(Expression> propertyExpression) 41 | { 42 | if (propertyExpression == null) 43 | throw new ArgumentNullException(nameof(propertyExpression)); 44 | 45 | var body = propertyExpression.Body as MemberExpression; 46 | if (body == null) 47 | throw new ArgumentException( 48 | "Argument is incomplete or broken", 49 | nameof(propertyExpression)); 50 | 51 | var property = body.Member as PropertyInfo; 52 | if (property == null) 53 | throw new ArgumentException( 54 | "Argument is not a property", 55 | nameof(propertyExpression)); 56 | 57 | return property.Name; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Windows; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ExpressiveAnnotations.MvvmDesktopSample")] 9 | [assembly: AssemblyCopyright("Copyright © Jarosław Waliszko 2014")] 10 | [assembly: AssemblyProduct("ExpressiveAnnotations.MvvmDesktopSample")] 11 | 12 | // Setting ComVisible to false makes the types in this assembly not visible 13 | // to COM components. If you need to access a type in this assembly from 14 | // COM, set the ComVisible attribute to true on that type. 15 | [assembly: ComVisible(false)] 16 | 17 | //In order to begin building localizable applications, set 18 | //CultureYouAreCodingWith in your .csproj file 19 | //inside a . For example, if you are using US english 20 | //in your source files, set the to en-US. Then uncomment 21 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 22 | //the line below to match the UICulture setting in the project file. 23 | 24 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 25 | 26 | 27 | [assembly: ThemeInfo( 28 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 29 | //(used if a resource is not found in the page, 30 | // or application resource dictionaries) 31 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 32 | //(used if a resource is not found in the page, 33 | // app, or any theme specific resource dictionaries) 34 | )] 35 | 36 | 37 | // Version information for an assembly consists of the following four values: 38 | // 39 | // Major Version 40 | // Minor Version 41 | // Build Number 42 | // Revision 43 | // 44 | // You can specify all the values or you can default the Build and Revision Numbers 45 | // by using the '*' as shown below: 46 | // [assembly: AssemblyVersion("1.0.*")] 47 | [assembly: AssemblyVersion("1.0.0.0")] 48 | [assembly: AssemblyFileVersion("1.0.0.0")] 49 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ExpressiveAnnotations.MvvmDesktopSample.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ExpressiveAnnotations.MvvmDesktopSample.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ExpressiveAnnotations.MvvmDesktopSample.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/README.md: -------------------------------------------------------------------------------- 1 | #### Sample application illustrating model validation in WPF MVVM using conditional data annotations. 2 | --- 3 | ![screenshot](screenshot.png "screenshot") 4 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Views/AddressView.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Views/AddressView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace ExpressiveAnnotations.MvvmDesktopSample.Views 4 | { 5 | /// 6 | /// Interaction logic for AddressView.xaml 7 | /// 8 | public partial class AddressView : UserControl 9 | { 10 | public AddressView() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Views/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace ExpressiveAnnotations.MvvmDesktopSample.Views 4 | { 5 | /// 6 | /// Interaction logic for App.xaml 7 | /// 8 | public partial class App : Application 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Views/ContactView.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | 25 | 26 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Views/ContactView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace ExpressiveAnnotations.MvvmDesktopSample.Views 4 | { 5 | /// 6 | /// Interaction logic for ContactView.xaml 7 | /// 8 | public partial class ContactView : UserControl 9 | { 10 | public ContactView() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Views/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | [more on github] 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Views/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Windows; 3 | using System.Windows.Navigation; 4 | using ExpressiveAnnotations.MvvmDesktopSample.Models; 5 | 6 | namespace ExpressiveAnnotations.MvvmDesktopSample.Views 7 | { 8 | /// 9 | /// Interaction logic for MainWindow.xaml 10 | /// 11 | public partial class MainWindow : Window 12 | { 13 | public MainWindow() 14 | { 15 | DataContext = new MainWindowVM(); 16 | InitializeComponent(); 17 | } 18 | 19 | private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e) 20 | { 21 | Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)); 22 | e.Handled = true; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/Views/QueryView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace ExpressiveAnnotations.MvvmDesktopSample.Views 4 | { 5 | /// 6 | /// Interaction logic for QueryView.xaml 7 | /// 8 | public partial class QueryView : UserControl 9 | { 10 | public QueryView() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvvmDesktopSample/favicon.ico -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.MvvmDesktopSample/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaliszko/ExpressiveAnnotations/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f/src/ExpressiveAnnotations.MvvmDesktopSample/screenshot.png -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.Tests/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.Tests/LocationComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ExpressiveAnnotations.Analysis; 3 | 4 | namespace ExpressiveAnnotations.Tests 5 | { 6 | public class LocationComparer: IEqualityComparer 7 | { 8 | public bool Equals(Location x, Location y) 9 | { 10 | return (x.Line == y.Line && x.Column == y.Column); 11 | } 12 | 13 | public int GetHashCode(Location obj) 14 | { 15 | return obj.GetHashCode(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("ExpressiveAnnotations.Tests")] 8 | [assembly: AssemblyCopyright("Copyright © Jarosław Waliszko 2014")] 9 | [assembly: AssemblyProduct("ExpressiveAnnotations.Tests")] 10 | 11 | // Setting ComVisible to false makes the types in this assembly not visible 12 | // to COM components. If you need to access a type in this assembly from 13 | // COM, set the ComVisible attribute to true on that type. 14 | [assembly: ComVisible(false)] 15 | 16 | // The following GUID is for the ID of the typelib if this project is exposed to COM 17 | [assembly: Guid("6b0bc28a-6af3-443a-856d-7e0090f2755a")] 18 | 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Build and Revision Numbers 27 | // by using the '*' as shown below: 28 | // [assembly: AssemblyVersion("1.0.*")] 29 | [assembly: AssemblyVersion("1.0.0.0")] 30 | [assembly: AssemblyFileVersion("1.0.0.0")] 31 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.Tests/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ExpressiveAnnotations.Tests { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | public class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | public static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ExpressiveAnnotations.Tests.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | public static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to default. 65 | /// 66 | public static string Lang { 67 | get { 68 | return ResourceManager.GetString("Lang", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to _{Value2}_. 74 | /// 75 | public static string Value2 { 76 | get { 77 | return ResourceManager.GetString("Value2", resourceCulture); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations/Analysis/Location.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | using System; 6 | 7 | namespace ExpressiveAnnotations.Analysis 8 | { 9 | /// 10 | /// Contains the location information related to some arbitrary data within associated text block. 11 | /// 12 | /// Used for pointing exact parsing error location in specified expression. 13 | /// 14 | /// 15 | [Serializable] 16 | public class Location 17 | { 18 | /// 19 | /// Initializes a new instance of the class. 20 | /// 21 | /// The line number. 22 | /// The column number. 23 | /// line <= 0 or column <= 0. 24 | public Location(int line, int column) 25 | { 26 | if (line < 1) 27 | throw new ArgumentOutOfRangeException(nameof(line), "Line number should be positive."); 28 | if (column < 1) 29 | throw new ArgumentOutOfRangeException(nameof(column), "Column number should be positive."); 30 | 31 | Line = line; 32 | Column = column; 33 | } 34 | 35 | /// 36 | /// Gets or sets the line number. 37 | /// 38 | public int Line { get; set; } 39 | 40 | /// 41 | /// Gets or sets the column number. 42 | /// 43 | public int Column { get; set; } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations/Analysis/Token.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | namespace ExpressiveAnnotations.Analysis 6 | { 7 | /// 8 | /// Token definition. 9 | /// 10 | public class Token 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// The token type. 16 | /// The token value. 17 | /// The token raw value. 18 | /// The token location within a specified expression. 19 | public Token(TokenType type, object value, string rawValue, Location location) 20 | { 21 | Type = type; 22 | Value = value; 23 | RawValue = rawValue; 24 | Location = location; 25 | } 26 | 27 | /// 28 | /// Gets the token type. 29 | /// 30 | public TokenType Type { get; private set; } 31 | 32 | /// 33 | /// Gets the token value (converted to appropriate type). 34 | /// 35 | public object Value { get; private set; } 36 | 37 | /// 38 | /// Gets the token raw value (not converted expression string). 39 | /// 40 | public string RawValue { get; private set; } 41 | 42 | /// 43 | /// Gets or sets the token location within a specified expression. 44 | /// 45 | public Location Location { get; private set; } 46 | 47 | /// 48 | /// Generates a human-reabable string for the token instance (handy for debugging purposes). 49 | /// 50 | /// 51 | /// A string that represents this instance. 52 | /// 53 | public override string ToString() 54 | { 55 | return $@"""{RawValue}"" {Type} ({Location.Line}, {Location.Column})"; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations/Attributes/AssertThatAttribute.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | using System; 6 | using System.ComponentModel.DataAnnotations; 7 | 8 | namespace ExpressiveAnnotations.Attributes 9 | { 10 | /// 11 | /// Validation attribute, executed for non-null annotated field, which indicates that assertion given 12 | /// in logical expression has to be satisfied, for such a field to be considered as valid. 13 | /// 14 | public sealed class AssertThatAttribute : ExpressiveAttribute 15 | { 16 | private static string _defaultErrorMessage = "Assertion for {0} field is not satisfied by the following logic: {1}"; 17 | 18 | /// 19 | /// Gets or sets the default error message. 20 | /// 21 | public static string DefaultErrorMessage 22 | { 23 | get { return _defaultErrorMessage; } 24 | set 25 | { 26 | if (value == null) 27 | throw new ArgumentNullException(nameof(value), "Default error message cannot be null."); 28 | _defaultErrorMessage = value; 29 | } 30 | } 31 | 32 | /// 33 | /// Initializes a new instance of the class. 34 | /// 35 | /// The logical expression based on which assertion condition is computed. 36 | public AssertThatAttribute(string expression) 37 | : base(expression, DefaultErrorMessage) 38 | { 39 | } 40 | 41 | /// 42 | /// Validates a specified value with respect to the associated validation attribute. 43 | /// Internally used by the method. 44 | /// 45 | /// The value to validate. 46 | /// The validation context. 47 | /// 48 | /// An instance of the class. 49 | /// 50 | protected override ValidationResult IsValidInternal(object value, ValidationContext validationContext) 51 | { 52 | if (value != null) 53 | { 54 | Compile(validationContext.ObjectType); 55 | if (!CachedValidationFuncs[validationContext.ObjectType](validationContext.ObjectInstance)) // check if the assertion condition is not satisfied 56 | return new ValidationResult( // assertion not satisfied => notify 57 | FormatErrorMessage(validationContext.DisplayName, Expression, validationContext.ObjectInstance), 58 | new[] {validationContext.MemberName}); 59 | } 60 | 61 | return ValidationResult.Success; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations/Attributes/ValueParserAttribute.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | using System; 6 | 7 | namespace ExpressiveAnnotations.Attributes 8 | { 9 | /// 10 | /// Provides a hint for client-side script pointing at parser name, which should be used for DOM field value deserialization. 11 | /// 12 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] 13 | public sealed class ValueParserAttribute : Attribute 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | /// Name of the parser. 19 | public ValueParserAttribute(string parserName) 20 | { 21 | ParserName = parserName; 22 | } 23 | 24 | /// 25 | /// Gets the name of the parser. 26 | /// 27 | public string ParserName { get; private set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations/Functions/IFunctionsProvider.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | using System.Collections.Generic; 6 | using System.Linq.Expressions; 7 | 8 | namespace ExpressiveAnnotations.Functions 9 | { 10 | /// 11 | /// Functions source. 12 | /// 13 | public interface IFunctionsProvider 14 | { 15 | /// 16 | /// Gets functions for the . 17 | /// 18 | /// 19 | /// Registered functions. 20 | /// 21 | IDictionary> GetFunctions(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations/MessageFormatter.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Text.RegularExpressions; 11 | 12 | namespace ExpressiveAnnotations 13 | { 14 | internal static class MessageFormatter 15 | { 16 | private const string _formatItemsRegex = @"({+)[_\p{L}]+(?:(?:\.[_\p{L}])?[_\p{L}\p{N}]*)*(?::(?:n|N))?(}+)"; // {fieldPath[:indicator]}, e.g. {field}, {field.field:n} (field path regex exactly as defined in lexer field) 17 | 18 | public static string FormatString(string input, out IList items) 19 | { 20 | Debug.Assert(input != null); 21 | 22 | items = new List(); 23 | var matches = Regex.Matches(input, _formatItemsRegex); 24 | var message = new StringBuilder(); 25 | 26 | Match prev = null; 27 | for (var i = 0; i < matches.Count; i++) 28 | { 29 | var match = matches[i]; 30 | var item = match.Value; 31 | var leftBraces = match.Groups[1]; 32 | var rightBraces = match.Groups[2]; 33 | 34 | if (leftBraces.Length != rightBraces.Length) 35 | throw new FormatException("Input string was not in a correct format."); 36 | 37 | var start = prev != null ? prev.Index + prev.Length : 0; 38 | var chars = match.Index - start; 39 | message.Append(input.Substring(start, chars)); 40 | prev = match; 41 | 42 | var added = items.SingleOrDefault(x => x.Body == item); 43 | if (added != null) 44 | { 45 | message.Append(added.Uuid); 46 | continue; 47 | } 48 | 49 | var length = leftBraces.Length; 50 | 51 | // flatten each pair of braces into single brace in order to escape them (just like string.Format() does) 52 | var leftBracesFlattened = new string('{', length / 2); 53 | var rightBracesFlattened = new string('}', length / 2); 54 | 55 | var uuid = Guid.NewGuid(); 56 | var param = item.Substring(length, item.Length - 2 * length); 57 | var current = new FormatItem 58 | { 59 | Uuid = uuid, 60 | Body = item, 61 | Constant = length % 2 == 0, 62 | FieldPath = param.Contains(":") ? param.Substring(0, param.IndexOf(":", StringComparison.Ordinal)) : param, 63 | Indicator = param.Contains(":") ? param.Substring(param.IndexOf(":", StringComparison.Ordinal) + 1) : null, 64 | Substitute = $"{leftBracesFlattened}{(length%2 != 0 ? uuid.ToString() : param)}{rightBracesFlattened}" // for odd number of braces, substitute param with respective value (just like string.Format() does) 65 | }; 66 | items.Add(current); 67 | message.Append(current.Uuid); 68 | } 69 | 70 | if (prev != null) 71 | message.Append(input.Substring(prev.Index + prev.Length)); 72 | 73 | return message.Length > 0 ? message.ToString() : input; 74 | } 75 | } 76 | 77 | internal class FormatItem 78 | { 79 | public Guid Uuid { get; set; } 80 | public string Body { get; set; } 81 | public bool Constant { get; set; } 82 | public string FieldPath { get; set; } 83 | public string Indicator { get; set; } 84 | public string Substitute { get; set; } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/ExpressiveAnnotations/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* https://github.com/jwaliszko/ExpressiveAnnotations 2 | * Copyright (c) 2014 Jarosław Waliszko 3 | * Licensed MIT: http://opensource.org/licenses/MIT */ 4 | 5 | using System.Reflection; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.InteropServices; 8 | 9 | // General Information about an assembly is controlled through the following 10 | // set of attributes. Change these attribute values to modify the information 11 | // associated with an assembly. 12 | [assembly: AssemblyTitle("ExpressiveAnnotations")] 13 | [assembly: AssemblyCopyright("Copyright © Jarosław Waliszko 2014")] 14 | [assembly: AssemblyProduct("ExpressiveAnnotations")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("4b679902-67d7-4463-a0f0-c4e75cb39ba6")] 23 | 24 | #if !SIGNED 25 | [assembly: InternalsVisibleTo("ExpressiveAnnotations.Tests")] 26 | [assembly: InternalsVisibleTo("ExpressiveAnnotations.MvcUnobtrusive")] 27 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // for mocks in unit tests 28 | #else 29 | [assembly: InternalsVisibleTo("ExpressiveAnnotations.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001000ff42e23a8247bbd1c54c6f4428d3e592e505391131b6e28a381dadbb26a88f8407d96afa9993877cd71d3b541470d8ebef5a8dcd780fccf270b1846e14d70b68732f98d8ba9dada92d1f128885fe903011a2185a4be124f5b00618413229f73692638e3c7c22d81cf9365f207a954c6b522183280c07011c325168c148865a4")] 30 | [assembly: InternalsVisibleTo("ExpressiveAnnotations.MvcUnobtrusive, PublicKey=00240000048000009400000006020000002400005253413100040000010001000ff42e23a8247bbd1c54c6f4428d3e592e505391131b6e28a381dadbb26a88f8407d96afa9993877cd71d3b541470d8ebef5a8dcd780fccf270b1846e14d70b68732f98d8ba9dada92d1f128885fe903011a2185a4be124f5b00618413229f73692638e3c7c22d81cf9365f207a954c6b522183280c07011c325168c148865a4")] 31 | // https://github.com/moq/moq4/wiki/Quickstart#advanced-features: 32 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] 33 | #endif 34 | 35 | // Version information for an assembly consists of the following four values: 36 | // 37 | // Major Version 38 | // Minor Version 39 | // Build Number 40 | // Revision 41 | // 42 | // You can specify all the values or you can default the Build and Revision Numbers 43 | // by using the '*' as shown below: 44 | // [assembly: AssemblyVersion("1.0.*")] 45 | [assembly: AssemblyVersion("2.7.4.0")] 46 | [assembly: AssemblyFileVersion("2.7.4.0")] 47 | -------------------------------------------------------------------------------- /src/form.tests.html: -------------------------------------------------------------------------------- 1 |  2 |
3 | 15 | 23 | 31 | 39 | 46 |
47 | --------------------------------------------------------------------------------