Browse Source

First Commit

Former-commit-id: 267292b5e198d1edec106c8b46222cdbcb9d8777
pull/17/head
James South 14 years ago
commit
2c734345e7
  1. 49
      .gitattributes
  2. 165
      .gitignore
  3. 59
      README.md
  4. 213
      src/ImageProcessor.Web/Caching/DiskCache.cs
  5. 76
      src/ImageProcessor.Web/Config/ImageCacheSection.cs
  6. 312
      src/ImageProcessor.Web/Config/ImageProcessingSection.cs
  7. 255
      src/ImageProcessor.Web/Config/ImageProcessorConfig.cs
  8. 182
      src/ImageProcessor.Web/Config/ImageSecuritySection.cs
  9. 58
      src/ImageProcessor.Web/Helpers/FileCompareLastwritetime.cs
  10. 346
      src/ImageProcessor.Web/Helpers/RemoteFile.cs
  11. 252
      src/ImageProcessor.Web/HttpModules/ImageProcessingModule.cs
  12. 50
      src/ImageProcessor.Web/ImageFactoryExtensions.cs
  13. 80
      src/ImageProcessor.Web/ImageProcessor.Web.csproj
  14. 96
      src/ImageProcessor.Web/ImageProcessor.Web.vsdoc
  15. 36
      src/ImageProcessor.Web/Properties/AssemblyInfo.cs
  16. 40
      src/ImageProcessor/Helpers/Extensions/EnumExtensions.cs
  17. 116
      src/ImageProcessor/Helpers/Extensions/StringExtensions.cs
  18. 498
      src/ImageProcessor/ImageFactory.cs
  19. 86
      src/ImageProcessor/ImageProcessor.csproj
  20. 39
      src/ImageProcessor/ImageProcessor.sln
  21. 7
      src/ImageProcessor/ImageProcessor.sln.vsdoc
  22. 103
      src/ImageProcessor/ImageProcessor.vsdoc
  23. 174
      src/ImageProcessor/Imaging/ImageUtils.cs
  24. 512
      src/ImageProcessor/Imaging/OctreeQuantizer.cs
  25. 138
      src/ImageProcessor/Imaging/PaletteQuantizer.cs
  26. 314
      src/ImageProcessor/Imaging/Quantizer.cs
  27. 62
      src/ImageProcessor/Imaging/ResponseType.cs
  28. 175
      src/ImageProcessor/Processors/Alpha.cs
  29. 210
      src/ImageProcessor/Processors/Crop.cs
  30. 292
      src/ImageProcessor/Processors/Filter.cs
  31. 159
      src/ImageProcessor/Processors/Format.cs
  32. 80
      src/ImageProcessor/Processors/IGraphicsProcessor.cs
  33. 147
      src/ImageProcessor/Processors/Png8.cs
  34. 142
      src/ImageProcessor/Processors/Quality.cs
  35. 235
      src/ImageProcessor/Processors/Resize.cs
  36. 200
      src/ImageProcessor/Processors/Vignette.cs
  37. 36
      src/ImageProcessor/Properties/AssemblyInfo.cs
  38. 20
      src/Test/Test.sln
  39. 324
      src/Test/Test/Content/Site.css
  40. BIN
      src/Test/Test/Content/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png
  41. BIN
      src/Test/Test/Content/themes/base/images/ui-bg_flat_75_ffffff_40x100.png
  42. BIN
      src/Test/Test/Content/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png
  43. BIN
      src/Test/Test/Content/themes/base/images/ui-bg_glass_65_ffffff_1x400.png
  44. BIN
      src/Test/Test/Content/themes/base/images/ui-bg_glass_75_dadada_1x400.png
  45. BIN
      src/Test/Test/Content/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png
  46. BIN
      src/Test/Test/Content/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png
  47. BIN
      src/Test/Test/Content/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png
  48. BIN
      src/Test/Test/Content/themes/base/images/ui-icons_222222_256x240.png
  49. BIN
      src/Test/Test/Content/themes/base/images/ui-icons_2e83ff_256x240.png
  50. BIN
      src/Test/Test/Content/themes/base/images/ui-icons_454545_256x240.png
  51. BIN
      src/Test/Test/Content/themes/base/images/ui-icons_888888_256x240.png
  52. BIN
      src/Test/Test/Content/themes/base/images/ui-icons_cd0a0a_256x240.png
  53. 24
      src/Test/Test/Content/themes/base/jquery.ui.accordion.css
  54. 16
      src/Test/Test/Content/themes/base/jquery.ui.all.css
  55. 62
      src/Test/Test/Content/themes/base/jquery.ui.autocomplete.css
  56. 11
      src/Test/Test/Content/themes/base/jquery.ui.base.css
  57. 43
      src/Test/Test/Content/themes/base/jquery.ui.button.css
  58. 46
      src/Test/Test/Content/themes/base/jquery.ui.core.css
  59. 73
      src/Test/Test/Content/themes/base/jquery.ui.datepicker.css
  60. 26
      src/Test/Test/Content/themes/base/jquery.ui.dialog.css
  61. 16
      src/Test/Test/Content/themes/base/jquery.ui.progressbar.css
  62. 25
      src/Test/Test/Content/themes/base/jquery.ui.resizable.css
  63. 15
      src/Test/Test/Content/themes/base/jquery.ui.selectable.css
  64. 29
      src/Test/Test/Content/themes/base/jquery.ui.slider.css
  65. 23
      src/Test/Test/Content/themes/base/jquery.ui.tabs.css
  66. 257
      src/Test/Test/Content/themes/base/jquery.ui.theme.css
  67. 193
      src/Test/Test/Controllers/AccountController.cs
  68. 48
      src/Test/Test/Controllers/HomeController.cs
  69. 1
      src/Test/Test/Global.asax
  70. 40
      src/Test/Test/Global.asax.cs
  71. BIN
      src/Test/Test/Images/1182076_e8c402e938_z.jpg
  72. 1
      src/Test/Test/Images/6287131503_9b1f82e4f0_b.jpg.REMOVED.git-id
  73. 1
      src/Test/Test/Images/Chrysanthemum.jpg.REMOVED.git-id
  74. 1
      src/Test/Test/Images/Chrysanthemum.png.REMOVED.git-id
  75. 1
      src/Test/Test/Images/Desert.jpg.REMOVED.git-id
  76. 1
      src/Test/Test/Images/Hydrangeas.jpg.REMOVED.git-id
  77. 1
      src/Test/Test/Images/Jellyfish.jpg.REMOVED.git-id
  78. 1
      src/Test/Test/Images/Koala.jpg.REMOVED.git-id
  79. 1
      src/Test/Test/Images/Lighthouse.jpg.REMOVED.git-id
  80. 1
      src/Test/Test/Images/MSwanson - Wide Large - Rock 02.jpg.REMOVED.git-id
  81. 1
      src/Test/Test/Images/Penguins.jpg.REMOVED.git-id
  82. 1
      src/Test/Test/Images/Tulips.jpg.REMOVED.git-id
  83. 1
      src/Test/Test/Images/fid11246.jpg.REMOVED.git-id
  84. 1
      src/Test/Test/Images/fid9141.jpg.REMOVED.git-id
  85. 1
      src/Test/Test/Images/war_horse_quad.jpg.REMOVED.git-id
  86. 67
      src/Test/Test/Models/AccountModels.cs
  87. 35
      src/Test/Test/Properties/AssemblyInfo.cs
  88. 1
      src/Test/Test/Scripts/MicrosoftAjax.debug.js.REMOVED.git-id
  89. 1
      src/Test/Test/Scripts/MicrosoftAjax.js.REMOVED.git-id
  90. 408
      src/Test/Test/Scripts/MicrosoftMvcAjax.debug.js
  91. 25
      src/Test/Test/Scripts/MicrosoftMvcAjax.js
  92. 883
      src/Test/Test/Scripts/MicrosoftMvcValidation.debug.js
  93. 55
      src/Test/Test/Scripts/MicrosoftMvcValidation.js
  94. 1
      src/Test/Test/Scripts/jquery-1.5.1-vsdoc.js.REMOVED.git-id
  95. 1
      src/Test/Test/Scripts/jquery-1.5.1.js.REMOVED.git-id
  96. 1
      src/Test/Test/Scripts/jquery-1.5.1.min.js.REMOVED.git-id
  97. 1
      src/Test/Test/Scripts/jquery-ui-1.8.11.js.REMOVED.git-id
  98. 1
      src/Test/Test/Scripts/jquery-ui-1.8.11.min.js.REMOVED.git-id
  99. 165
      src/Test/Test/Scripts/jquery.unobtrusive-ajax.js
  100. 5
      src/Test/Test/Scripts/jquery.unobtrusive-ajax.min.js

49
.gitattributes

@ -0,0 +1,49 @@
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.jpg binary
*.png binary
*.gif binary
*.cs text=auto diff=csharp
*.vb text=auto
*.c text=auto
*.cpp text=auto
*.cxx text=auto
*.h text=auto
*.hxx text=auto
*.py text=auto
*.rb text=auto
*.java text=auto
*.html text=auto
*.htm text=auto
*.css text=auto
*.scss text=auto
*.sass text=auto
*.less text=auto
*.js text=auto
*.lisp text=auto
*.clj text=auto
*.sql text=auto
*.php text=auto
*.lua text=auto
*.m text=auto
*.asm text=auto
*.erl text=auto
*.fs text=auto
*.fsx text=auto
*.hs text=auto
*.csproj text=auto merge=union
*.vbproj text=auto merge=union
*.fsproj text=auto merge=union
*.dbproj text=auto merge=union
*.sln text=auto eol=crlf merge=union

165
.gitignore

@ -0,0 +1,165 @@
#################
## Eclipse
#################
*.pydevproject
.project
.metadata
bin/**
tmp/**
tmp/**/*
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
#################
## Visual Studio
#################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
**/[Dd]ebug/
**/[Rr]elease/
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.vspscc
.builds
**/*.dotCover
## TODO: If you have NuGet Package Restore enabled, uncomment this
#**/packages/
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
# Visual Studio profiler
*.psess
*.vsp
# ReSharper is a .NET coding add-in
_ReSharper*
# Installshield output folder
[Ee]xpress
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish
# Others
[Bb]in
[Oo]bj
sql
TestResults
*.Cache
ClientBin
stylecop.*
~$*
*.dbmdl
Generated_Code #added for RIA/Silverlight projects
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
############
## Windows
############
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
#############
## Python
#############
*.py[co]
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
#Translations
*.mo
#Mr Developer
.mr.developer.cfg
# Mac crap
.DS_Store

59
README.md

@ -0,0 +1,59 @@
ImageProcessor
===============
ImageProcessor is a library for on the fly processing of image files using Asp.Net
The library architecture is highly extensible and allows for easy extension.
Core plugins at present include:
- Resize
- Crop
- Quality (The quality to set the output for jpeg files)
- Filter (Image filters including sepia, greyscale, blackwhite, lomograph)
- Vignette
- Format (Sets the output format)
- Alpha (Sets opacity)
The library consists of two binaries: ImageProcessor.dll and ImageProcessor.Web.dll.
ImageProcessor.dll contains all the core functionality that allows for image manipulation via the `ImageFactory` class. This has a fluent API which allows you to easily chain methods to deliver the desired output.
e.g.
// Read a file and resize it.
var photoBytes = File.ReadAllBytes(file);
var before = DateTime.Now;
var quality = 90;
var format = ImageFormat.Jpeg;
var thumbnailSize = 150;
byte[] resized;
using (var inStream = new MemoryStream(photoBytes))
{
using (var outStream = new MemoryStream())
{
using (ImageFactory imageFactory = new ImageFactory())
{
// Load, resize and save an image.
imageFactory.Load(inStream).Format(format).Quality(quality).Resize(thumbnailSize, 0).Save(outStream);
}
resized = outStream.ToArray();
}
}
ImageProcessor.Web.dll contains a HttpModule which captures internal and external requests automagically processing them based on values captured through querystring parameters.
Using the HttpModule requires no code writing at all. Just reference the binaries and add the relevant sections to the web.config
Image requests suffixed with QueryString parameters will then be processed and cached to the server allowing for easy and efficient parsing of following requests.
e.g.
<img src="/images.yourimage.jpg?width=200" alt="your resized image"/>
Will resize your image to 200px wide whilst keeping the correct aspect ratio.

213
src/ImageProcessor.Web/Caching/DiskCache.cs

@ -0,0 +1,213 @@
// -----------------------------------------------------------------------
// <copyright file="DiskCache.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Web.Caching
{
#region Using
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Hosting;
using ImageProcessor.Helpers.Extensions;
using ImageProcessor.Web.Config;
using ImageProcessor.Web.Helpers;
#endregion
/// <summary>
/// Encapsulates methods to handle disk caching of images.
/// </summary>
internal sealed class DiskCache
{
#region Fields
/// <summary>
/// The maximum number or time a new file should be cached before checking the
/// cache controller and running any clearing mechanisms.
/// </summary>
/// <remarks>
/// NTFS file systems can handle up to 8000 files in one directory. The Cache controller will clear out any
/// time we hit 6000 so if we tell the handler to run at every 1000 times an image is added to the cache we
/// should have a 1000 file buffer.
/// </remarks>
internal const int MaxRunsBeforeCacheClear = 1000;
/// <summary>
/// The maximum number of days to cache files on the system for.
/// </summary>
internal static readonly int MaxFileCachedDuration = ImageProcessorConfig.Instance.MaxCacheDays;
/// <summary>
/// The object to lock against.
/// </summary>
private static readonly object SyncRoot = new object();
/// <summary>
/// The default paths for Cached folders on the server.
/// </summary>
private static readonly string CachePath = ImageProcessorConfig.Instance.VirtualCachePath;
/// <summary>
/// The maximum number of files allowed in the directory.
/// </summary>
/// <remarks>
/// NTFS Folder can handle up to 8000 files in a directory.
/// This buffer will help us to ensure that we rarely hit anywhere near that limit.
/// </remarks>
private const int MaxFilesCount = 6000;
#endregion
#region Methods
/// <summary>
/// Gets the full transformed cached path for the image.
/// </summary>
/// <param name="imagePath">The original image path.</param>
/// <param name="imageName">The original image name.</param>
/// <returns>The full cached path for the image.</returns>
internal static string GetCachePath(string imagePath, string imageName)
{
string virtualCachePath = CachePath;
string absoluteCachePath = HostingEnvironment.MapPath(virtualCachePath);
string cachedPath = string.Empty;
if (absoluteCachePath != null)
{
DirectoryInfo di = new DirectoryInfo(absoluteCachePath);
if (!di.Exists)
{
// Create the directory.
Directory.CreateDirectory(absoluteCachePath);
}
string cachedFileName = string.Format("{0}{1}", imagePath.ToMD5Fingerprint(), imageName.Substring(imageName.LastIndexOf(".", StringComparison.Ordinal)));
cachedPath = Path.Combine(absoluteCachePath, cachedFileName);
}
return cachedPath;
}
/// <summary>
/// Converts an absolute file path
/// </summary>
/// <param name="absolutePath">The absolute path to convert.</param>
/// <param name="request">The <see cref="T:System.Web.HttpRequest"/>from the current context.</param>
/// <returns>The virtual path to the file.</returns>
internal static string GetVirtualPath(string absolutePath, HttpRequest request)
{
string applicationPath = request.PhysicalApplicationPath;
string virtualDir = request.ApplicationPath;
virtualDir = virtualDir == "/" ? virtualDir : (virtualDir + "/");
if (applicationPath != null)
{
return absolutePath.Replace(applicationPath, virtualDir).Replace(@"\", "/");
}
throw new InvalidOperationException("We can only map an absolute back to a relative path if the application path is available.");
}
/// <summary>
/// Purges any files from the filesystem cache in a background thread.
/// </summary>
internal static void PurgeCachedFolders()
{
ThreadStart threadStart = PurgeFolders;
Thread thread = new Thread(threadStart)
{
IsBackground = true
};
thread.Start();
}
/// <summary>
/// Returns a value indicating whether the original file has been updated.
/// </summary>
/// <param name="imagePath">The original image path.</param>
/// <param name="cachedImagePath">The cached image path.</param>
/// <returns>
/// True if the the original file has been updated; otherwise, false.
/// </returns>
internal static bool IsUpdatedFile(string imagePath, string cachedImagePath)
{
if (File.Exists(imagePath) && File.Exists(cachedImagePath))
{
FileInfo imageFileInfo = new FileInfo(imagePath);
FileInfo cachedImageFileInfo = new FileInfo(cachedImagePath);
return !new FileCompareLastwritetime().Equals(imageFileInfo, cachedImageFileInfo);
}
return true;
}
/// <summary>
/// Sets the LastWriteTime of the cached file to match the original file.
/// </summary>
/// <param name="imagePath">The original image path.</param>
/// <param name="cachedImagePath">The cached image path.</param>
internal static void SetCachedLastWriteTime(string imagePath, string cachedImagePath)
{
if (File.Exists(imagePath) && File.Exists(cachedImagePath))
{
lock (SyncRoot)
{
DateTime dateTime = File.GetLastWriteTime(imagePath);
File.SetLastWriteTime(cachedImagePath, dateTime);
}
}
}
/// <summary>
/// Purges any files from the filesystem cache in the given folders.
/// </summary>
private static void PurgeFolders()
{
string folder = HostingEnvironment.MapPath(CachePath);
if (folder != null)
{
DirectoryInfo directoryInfo = new DirectoryInfo(folder);
if (directoryInfo.Exists)
{
// Get all the files in the cache ordered by LastAccessTime - oldest first.
List<FileInfo> fileInfos = directoryInfo.EnumerateFiles("*", SearchOption.AllDirectories)
.OrderBy(x => x.LastAccessTime).ToList();
int counter = fileInfos.Count;
Parallel.ForEach(
fileInfos,
fileInfo =>
{
lock (SyncRoot)
{
try
{
// Delete the file if we are nearing our limit buffer.
if (counter >= MaxFilesCount || fileInfo.LastAccessTime < DateTime.Now.AddDays(-MaxFileCachedDuration))
{
fileInfo.Delete();
counter -= 1;
}
}
catch
{
// TODO: Sort out the try/catch.
throw;
}
}
});
}
}
}
#endregion
}
}

76
src/ImageProcessor.Web/Config/ImageCacheSection.cs

@ -0,0 +1,76 @@
// -----------------------------------------------------------------------
// <copyright file="ImageCacheSection.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Web.Config
{
#region Using
using System.Configuration;
using ImageProcessor.Helpers.Extensions;
#endregion
/// <summary>
/// Represents an imagecache section within a configuration file.
/// </summary>
public class ImageCacheSection : ConfigurationSection
{
/// <summary>
/// Gets or sets the virtual path of the cache folder.
/// </summary>
/// <value>The name of the cache folder.</value>
[ConfigurationProperty("virtualPath", DefaultValue = "~/cache", IsRequired = true)]
[StringValidator(MinLength = 3, MaxLength = 200)]
public string VirtualPath
{
get
{
string virtualPath = (string)this["virtualPath"];
return virtualPath.IsValidVirtualPathName() ? virtualPath : "~/cache";
}
set
{
this["virtualPath"] = value;
}
}
/// <summary>
/// Gets or sets the maximum number of days to store an image in the cache.
/// </summary>
/// <value>The maximum number of days to store an image in the cache.</value>
/// <remarks>Defaults to 7 if not set. Maximum of 28.</remarks>
[ConfigurationProperty("maxDays", DefaultValue = "7", IsRequired = false)]
[IntegerValidator(ExcludeRange = false, MaxValue = 28, MinValue = 0)]
public int MaxDays
{
get
{
return (int)this["maxDays"];
}
set
{
this["maxDays"] = value;
}
}
/// <summary>
/// Retrieves the cache configuration section from the current application configuration.
/// </summary>
/// <returns>The cache configuration section from the current application configuration.</returns>
public static ImageCacheSection GetConfiguration()
{
ImageCacheSection imageCacheSection = ConfigurationManager.GetSection("imageProcessor/cache") as ImageCacheSection;
if (imageCacheSection != null)
{
return imageCacheSection;
}
return new ImageCacheSection();
}
}
}

312
src/ImageProcessor.Web/Config/ImageProcessingSection.cs

@ -0,0 +1,312 @@
// -----------------------------------------------------------------------
// <copyright file="ImageProcessingSection.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Web.Config
{
#region Using
using System.Configuration;
using System.Linq;
#endregion
/// <summary>
/// Represents an imageprocessing section within a configuration file.
/// Nested syntax adapted from http://tneustaedter.blogspot.co.uk/2011/09/how-to-create-one-or-more-nested.html
/// </summary>
public class ImageProcessingSection : ConfigurationSection
{
#region Properties
/// <summary>
/// Gets the <see cref="T:ImageProcessor.Web.Config.ImageProcessingSection.PluginElementCollection"/>.
/// </summary>
/// <value>
/// The <see cref="T:ImageProcessor.Web.Config.ImageProcessingSection.PluginElementCollection"/>.
/// </value>
[ConfigurationProperty("plugins", IsRequired = true)]
public PluginElementCollection Plugins
{
get
{
return this["plugins"] as PluginElementCollection;
}
}
#endregion
#region Methods
/// <summary>
/// Retrieves the processing configuration section from the current application configuration.
/// </summary>
/// <returns>The processing configuration section from the current application configuration. </returns>
public static ImageProcessingSection GetConfiguration()
{
ImageProcessingSection imageProcessingSection =
ConfigurationManager.GetSection("imageProcessor/processing") as ImageProcessingSection;
if (imageProcessingSection != null)
{
return imageProcessingSection;
}
return new ImageProcessingSection();
}
#endregion
/// <summary>
/// Represents a PluginElement configuration element within the configuration.
/// </summary>
public class PluginElement : ConfigurationElement
{
/// <summary>
/// Gets or sets the name of the plugin file.
/// </summary>
/// <value>The name of the plugin.</value>
[ConfigurationProperty("name", DefaultValue = "", IsRequired = true)]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
/// <summary>
/// Gets the <see cref="T:ImageProcessor.Web.Config.ImageProcessingSection.SettingElementCollection"/>.
/// </summary>
/// <value>
/// The <see cref="T:ImageProcessor.Web.Config.ImageProcessingSection.SettingElementCollection"/>.
/// </value>
[ConfigurationProperty("settings", IsRequired = true)]
public SettingElementCollection Settings
{
get
{
return this["settings"] as SettingElementCollection;
}
}
}
/// <summary>
/// Represents a PluginElementCollection collection configuration element within the configuration.
/// </summary>
public class PluginElementCollection : ConfigurationElementCollection
{
/// <summary>
/// Gets the type of the <see cref="T:System.Configuration.ConfigurationElementCollection"/>.
/// </summary>
/// <value>
/// The <see cref="T:System.Configuration.ConfigurationElementCollectionType"/> of this collection.
/// </value>
public override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMap; }
}
/// <summary>
/// Gets the name used to identify this collection of elements in the configuration file when overridden in a derived class.
/// </summary>
/// <value>
/// The name of the collection; otherwise, an empty string. The default is an empty string.
/// </value>
protected override string ElementName
{
get { return "plugin"; }
}
/// <summary>
/// Gets or sets the <see cref="T:ImageProcessor.Web.Config.ImageProcessingSection.PluginElement"/>
/// at the specified index within the collection.
/// </summary>
/// <param name="index">The index at which to get the specified object.</param>
/// <returns>
/// The the <see cref="T:ImageProcessor.Web.Config.ImageProcessingSection.PluginElement"/>
/// at the specified index within the collection.
/// </returns>
public PluginElement this[int index]
{
get
{
return (PluginElement)BaseGet(index);
}
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
/// <summary>
/// Creates a new PluginConfig configuration element.
/// </summary>
/// <returns>
/// A new PluginConfig configuration element.
/// </returns>
protected override ConfigurationElement CreateNewElement()
{
return new PluginElement();
}
/// <summary>
/// Gets the element key for a specified PluginElement configuration element.
/// </summary>
/// <param name="element">
/// The <see cref="T:System.Configuration.ConfigurationElement">ConfigurationElement</see>
/// to return the key for.
/// </param>
/// <returns>The element key for a specified PluginElement configuration element.</returns>
protected override object GetElementKey(ConfigurationElement element)
{
return ((PluginElement)element).Name;
}
}
/// <summary>
/// Represents a SettingElement configuration element within the configuration.
/// </summary>
public class SettingElement : ConfigurationElement
{
/// <summary>
/// Gets or sets the key of the plugin setting.
/// </summary>
/// <value>The key of the plugin setting.</value>
[ConfigurationProperty("key", IsRequired = true, IsKey = true)]
public string Key
{
get
{
return this["key"] as string;
}
set
{
this["key"] = value;
}
}
/// <summary>
/// Gets or sets the value of the plugin setting.
/// </summary>
/// <value>The value of the plugin setting.</value>
[ConfigurationProperty("value", IsRequired = true)]
public string Value
{
get
{
return (string)this["value"];
}
set
{
this["value"] = value;
}
}
}
/// <summary>
/// Represents a SettingElementCollection collection configuration element within the configuration.
/// </summary>
public class SettingElementCollection : ConfigurationElementCollection
{
/// <summary>
/// Gets the type of the <see cref="T:System.Configuration.ConfigurationElementCollection"/>.
/// </summary>
/// <value>
/// The <see cref="T:System.Configuration.ConfigurationElementCollectionType"/> of this collection.
/// </value>
public override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMap; }
}
/// <summary>
/// Gets the name used to identify this collection of elements in the configuration file when overridden in a derived class.
/// </summary>
/// <value>
/// The name of the collection; otherwise, an empty string. The default is an empty string.
/// </value>
protected override string ElementName
{
get { return "setting"; }
}
/// <summary>
/// Gets or sets the <see cref="T:ImageProcessor.Web.Config.ImageProcessingSection.SettingElement"/>
/// at the specified index within the collection.
/// </summary>
/// <param name="index">The index at which to get the specified object.</param>
/// <returns>
/// The the <see cref="T:ImageProcessor.Web.Config.ImageProcessingSection.SettingElement"/>
/// at the specified index within the collection.
/// </returns>
public SettingElement this[int index]
{
get
{
return (SettingElement)BaseGet(index);
}
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
/// <summary>
/// Returns the setting element with the specified key.
/// </summary>
/// <param name="key">knkn knk</param>
/// <returns>jn jnj </returns>
public new SettingElement this[string key]
{
get { return (SettingElement)BaseGet(key); }
}
/// <summary>
/// Returns a value indicating whether the settings collection contains the
/// given object.
/// </summary>
/// <param name="key">The key to identify the setting.</param>
/// <returns>True if the collection contains the key; otherwise false.</returns>
public bool ContainsKey(string key)
{
object[] keys = BaseGetAllKeys();
return keys.Any(obj => (string)obj == key);
}
/// <summary>
/// Gets the element key for a specified PluginElement configuration element.
/// </summary>
/// <param name="element">
/// The <see cref="T:System.Configuration.ConfigurationElement">ConfigurationElement</see>
/// to return the key for.
/// </param>
/// <returns>The element key for a specified PluginElement configuration element.</returns>
protected override object GetElementKey(ConfigurationElement element)
{
return ((SettingElement)element).Key;
}
/// <summary>
/// Creates a new SettingElement configuration element.
/// </summary>
/// <returns>
/// A new SettingElement configuration element.
/// </returns>
protected override ConfigurationElement CreateNewElement()
{
return new SettingElement();
}
}
}
}

255
src/ImageProcessor.Web/Config/ImageProcessorConfig.cs

@ -0,0 +1,255 @@
// -----------------------------------------------------------------------
// <copyright file="ImageProcessorConfig.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Web.Config
{
#region Using
using System;
using System.Collections.Generic;
using System.Linq;
using ImageProcessor.Processors;
#endregion
/// <summary>
/// Encapsulates methods to allow the retrieval of imageprocessor settings.
/// http://csharpindepth.com/Articles/General/Singleton.aspx
/// </summary>
public class ImageProcessorConfig
{
#region Fields
/// <summary>
/// A new instance Initializes a new instance of the <see cref="T:ImageProcessor.Web.Config.ImageProcessorConfig"/> class.
/// intitialized lazily.
/// </summary>
private static readonly Lazy<ImageProcessorConfig> Lazy =
new Lazy<ImageProcessorConfig>(() => new ImageProcessorConfig());
/// <summary>
/// A collection of the <see cref="T:ImageProcessor.Web.Config.ImageProcessingSection.SettingElementCollection"/> elements
/// for available plugins.
/// </summary>
private static readonly Dictionary<string, Dictionary<string, string>> PluginSettings =
new Dictionary<string, Dictionary<string, string>>();
/// <summary>
/// The processing configuration section from the current application configuration.
/// </summary>
private static ImageProcessingSection imageProcessingSection;
/// <summary>
/// The cache configuration section from the current application configuration.
/// </summary>
private static ImageCacheSection imageCacheSection;
/// <summary>
/// The security configuration section from the current application configuration.
/// </summary>
private static ImageSecuritySection imageSecuritySection;
#endregion
#region Constructors
/// <summary>
/// Prevents a default instance of the <see cref="T:ImageProcessor.Web.Config.ImageProcessorConfig"/> class from being created.
/// </summary>
private ImageProcessorConfig()
{
this.LoadGraphicsProcessors();
}
#endregion
#region Properties
/// <summary>
/// Gets the current instance of the <see cref="T:ImageProcessor.Web.Config.ImageProcessorConfig"/> class.
/// </summary>
public static ImageProcessorConfig Instance
{
get
{
return Lazy.Value;
}
}
/// <summary>
/// Gets the list of available GraphicsProcessors.
/// </summary>
public List<IGraphicsProcessor> GraphicsProcessors { get; private set; }
#region Caching
/// <summary>
/// Gets the maximum number of days to store images in the cache.
/// </summary>
public int MaxCacheDays
{
get
{
return GetImageCacheSection().MaxDays;
}
}
/// <summary>
/// Gets or the virtual path of the cache folder.
/// </summary>
/// <value>The virtual path of the cache folder.</value>
public string VirtualCachePath
{
get
{
return GetImageCacheSection().VirtualPath;
}
}
#endregion
#region Security
/// <summary>
/// Gets a list of whitelisted urls that images can be downloaded from.
/// </summary>
public Uri[] RemoteFileWhiteList
{
get
{
return GetImageSecuritySection().WhiteList.Cast<ImageSecuritySection.SafeURL>().Select(x => x.Url).ToArray();
}
}
/// <summary>
/// Gets a value indicating whether the current application is allowed to download remote files.
/// </summary>
public bool AllowRemoteDownloads
{
get
{
return GetImageSecuritySection().AllowRemoteDownloads;
}
}
/// <summary>
/// Gets the maximum length to wait in milliseconds before throwing an error requesting a remote file.
/// </summary>
public int Timeout
{
get
{
return GetImageSecuritySection().Timeout;
}
}
/// <summary>
/// Gets the maximum allowable size in bytes of e remote file to process.
/// </summary>
public int MaxBytes
{
get
{
return GetImageSecuritySection().MaxBytes;
}
}
/// <summary>
/// Gets the remote prefix for external files for the application.
/// </summary>
public string RemotePrefix
{
get
{
return GetImageSecuritySection().RemotePrefix;
}
}
#endregion
#endregion
#region Methods
/// <summary>
/// Returns the <see cref="T:ImageProcessor.Web.Config.ImageProcessingSection.SettingElementCollection"/> for the given plugin.
/// </summary>
/// <param name="name">
/// The name of the plugin to get the settings for.
/// </param>
/// <returns>
/// The <see cref="T:ImageProcessor.Web.Config.ImageProcessingSection.SettingElementCollection"/> for the given plugin.
/// </returns>
public Dictionary<string, string> GetPluginSettings(string name)
{
if (!PluginSettings.ContainsKey(name))
{
var pluginElement =
GetImageProcessingSection().Plugins
.Cast<ImageProcessingSection.PluginElement>()
.FirstOrDefault(x => x.Name == name);
Dictionary<string, string> settings;
if (pluginElement != null)
{
settings = pluginElement.Settings
.Cast<ImageProcessingSection.SettingElement>()
.ToDictionary(setting => setting.Key, setting => setting.Value);
}
else
{
settings = new Dictionary<string, string>();
}
PluginSettings.Add(name, settings);
return settings;
}
return PluginSettings[name];
}
/// <summary>
/// Retrieves the processing configuration section from the current application configuration.
/// </summary>
/// <returns>The processing configuration section from the current application configuration. </returns>
private static ImageProcessingSection GetImageProcessingSection()
{
return imageProcessingSection ?? (imageProcessingSection = ImageProcessingSection.GetConfiguration());
}
/// <summary>
/// Retrieves the caching configuration section from the current application configuration.
/// </summary>
/// <returns>The caching configuration section from the current application configuration. </returns>
private static ImageCacheSection GetImageCacheSection()
{
return imageCacheSection ?? (imageCacheSection = ImageCacheSection.GetConfiguration());
}
/// <summary>
/// Retrieves the security configuration section from the current application configuration.
/// </summary>
/// <returns>The security configuration section from the current application configuration. </returns>
private static ImageSecuritySection GetImageSecuritySection()
{
return imageSecuritySection ?? (imageSecuritySection = ImageSecuritySection.GetConfiguration());
}
/// <summary>
/// Gets the list of available GraphicsProcessors.
/// </summary>
private void LoadGraphicsProcessors()
{
if (this.GraphicsProcessors == null)
{
// Build a list of native IGraphicsProcessor instances.
Type type = typeof(IGraphicsProcessor);
IEnumerable<Type> types =
AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes()).Where(
p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract).ToList();
// Create them and add.
this.GraphicsProcessors =
types.Select(x => (Activator.CreateInstance(x) as IGraphicsProcessor)).ToList();
// Add the available settings.
foreach (IGraphicsProcessor processor in this.GraphicsProcessors)
{
processor.Settings = this.GetPluginSettings(processor.Name);
}
}
}
#endregion
}
}

182
src/ImageProcessor.Web/Config/ImageSecuritySection.cs

@ -0,0 +1,182 @@
// -----------------------------------------------------------------------
// <copyright file="ImageSecuritySection.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Web.Config
{
#region Using
using System;
using System.Configuration;
#endregion
/// <summary>
/// Represents an imagecache section within a configuration file.
/// </summary>
public class ImageSecuritySection : ConfigurationSection
{
#region Properties
/// <summary>
/// Gets or sets a value indicating whether the current application is allowed download remote files.
/// </summary>
/// <value><see langword="true"/> if the current application is allowed download remote files; otherwise, <see langword="false"/>.</value>
[ConfigurationProperty("allowRemoteDownloads", DefaultValue = false, IsRequired = true)]
public bool AllowRemoteDownloads
{
get { return (bool)this["allowRemoteDownloads"]; }
set { this["allowRemoteDownloads"] = value; }
}
/// <summary>
/// Gets or sets the maximum allowed remote file timeout in milliseconds for the application.
/// </summary>
/// <value>The maximum number of days to store an image in the cache.</value>
/// <remarks>Defaults to 30000 (30 seconds) if not set.</remarks>
[ConfigurationProperty("timeout", DefaultValue = "300000", IsRequired = true)]
public int Timeout
{
get
{
return (int)this["timeout"];
}
set
{
this["timeout"] = value;
}
}
/// <summary>
/// Gets or sets the maximum allowed remote file size in bytes for the application.
/// </summary>
/// <value>The maximum number of days to store an image in the cache.</value>
/// <remarks>Defaults to 524288 (512kb) if not set.</remarks>
[ConfigurationProperty("maxBytes", DefaultValue = "524288", IsRequired = true)]
public int MaxBytes
{
get
{
return (int)this["maxBytes"];
}
set
{
this["maxBytes"] = value;
}
}
/// <summary>
/// Gets or sets the prefix for remote files for the application.
/// </summary>
/// <value>The prefix for remote files for the application.</value>
[ConfigurationProperty("remotePrefix", DefaultValue = "", IsRequired = true)]
public string RemotePrefix
{
get { return (string)this["remotePrefix"]; }
set { this["remotePrefix"] = value; }
}
/// <summary>
/// Gets the <see cref="T:ImageProcessor.Web.Config.ImageSecuritySection.WhiteListElementCollection"/>
/// </summary>
/// <value>The <see cref="T:ImageProcessor.Web.Config.ImageSecuritySection.WhiteListElementCollection"/></value>
[ConfigurationProperty("whiteList", IsRequired = true)]
public WhiteListElementCollection WhiteList
{
get
{
object o = this["whiteList"];
return o as WhiteListElementCollection;
}
}
#endregion
#region Methods
/// <summary>
/// Retrieves the security configuration section from the current application configuration.
/// </summary>
/// <returns>The cache configuration section from the current application configuration.</returns>
public static ImageSecuritySection GetConfiguration()
{
ImageSecuritySection imageSecuritySection = ConfigurationManager.GetSection("imageProcessor/security") as ImageSecuritySection;
if (imageSecuritySection != null)
{
return imageSecuritySection;
}
return new ImageSecuritySection();
}
#endregion
/// <summary>
/// Represents a whitelist collection configuration element within the configuration.
/// </summary>
public class WhiteListElementCollection : ConfigurationElementCollection
{
/// <summary>
/// Gets or sets the whitelist item at the given index.
/// </summary>
/// <param name="index">The index of the whitelist item to get.</param>
/// <returns>The whitelist item at the given index.</returns>
public SafeURL this[int index]
{
get
{
return this.BaseGet(index) as SafeURL;
}
set
{
if (this.BaseGet(index) != null)
{
this.BaseRemoveAt(index);
}
this.BaseAdd(index, value);
}
}
/// <summary>
/// Creates a new SafeURL configuration element.
/// </summary>
/// <returns>
/// A new SafeURL configuration element.
/// </returns>
protected override ConfigurationElement CreateNewElement()
{
return new SafeURL();
}
/// <summary>
/// Gets the element key for a specified whitelist configuration element.
/// </summary>
/// <param name="element">The <see cref="T:System.Configuration.ConfigurationElement">ConfigurationElement</see> to return the key for.</param>
/// <returns>The element key for a specified whitelist configuration element.</returns>
protected override object GetElementKey(ConfigurationElement element)
{
return ((SafeURL)element).Url;
}
}
/// <summary>
/// Represents a whitelist configuration element within the configuration.
/// </summary>
public class SafeURL : ConfigurationElement
{
/// <summary>
/// Gets or sets the url of the whitelisted file.
/// </summary>
/// <value>The url of the whitelisted file.</value>
[ConfigurationProperty("url", DefaultValue = "", IsRequired = true)]
public Uri Url
{
get { return (Uri)this["url"]; }
set { this["url"] = value; }
}
}
}
}

58
src/ImageProcessor.Web/Helpers/FileCompareLastwritetime.cs

@ -0,0 +1,58 @@
// -----------------------------------------------------------------------
// <copyright file="FileCompareLastwritetime.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Web.Helpers
{
#region Using
using System;
using System.Collections.Generic;
#endregion
/// <summary>
/// Encapsulates methods to support the comparison of <see cref="T:System.IO.FileInfo"/> objects for equality.
/// </summary>
public class FileCompareLastwritetime : IEqualityComparer<System.IO.FileInfo>
{
/// <summary>
/// Converts the value of the current <see cref="T:System.DateTime"/> object to its equivalent
/// nearest minute representation.
/// </summary>
/// <param name="value">An instance of <see cref="T:System.DateTime"/>.</param>
/// <returns>
/// A value of the current <see cref="T:System.DateTime"/> object to its equivalent
/// nearest minute representation.
/// </returns>
public static DateTime ToMinute(DateTime value)
{
return new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, 0, value.Kind);
}
/// <summary>
/// Determines whether the specified instances of <see cref="T:System.IO.FileInfo"/> object are equal.
/// </summary>
/// <param name="f1">
/// The first <see cref="T:System.IO.FileInfo"/> object to compare.
/// </param>
/// <param name="f2">
/// The second <see cref="T:System.IO.FileInfo"/> object to compare.
/// </param>
/// <returns>true if the specified objects are equal; otherwise, false.</returns>
public bool Equals(System.IO.FileInfo f1, System.IO.FileInfo f2)
{
return ToMinute(f1.LastWriteTime) == ToMinute(f2.LastWriteTime);
}
/// <summary>
/// Returns a hash code for the specified <see cref="T:System.IO.FileInfo"/>.
/// </summary>
/// <param name="fi">The FileInfo to return the hashcode for.</param>
/// <returns>A hash code for the specified <see cref="T:System.IO.FileInfo"/>.</returns>
public int GetHashCode(System.IO.FileInfo fi)
{
return ToMinute(fi.LastWriteTime).GetHashCode();
}
}
}

346
src/ImageProcessor.Web/Helpers/RemoteFile.cs

@ -0,0 +1,346 @@
// -----------------------------------------------------------------------
// <copyright file="RemoteFile.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Web.Helpers
{
#region Using
using System;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Security;
using System.Text;
using ImageProcessor.Web.Config;
#endregion
/// <summary>
/// Encapsulates methods used to download files from a website address.
/// </summary>
/// <remarks>
/// <para>
/// The purpose of this class is so there's one core way of downloading remote files with urls that are from
/// outside users. There's various areas in application where an attacker could supply an external url to the server
/// and tie up resources.
/// </para>
/// For example, the ImageProcessingModule accepts off-server addresses as a path. An attacker could, for instance, pass the url
/// to a file that's a few gigs in size, causing the server to get out-of-memory exceptions or some other errors. An attacker
/// could also use this same method to use one application instance to hammer another site by, again, passing an off-server
/// address of the victims site to the ImageProcessingModule.
/// This class will not throw an exception if the Uri supplied points to a resource local to the running application instance.
/// <para>
/// There shouldn't be any security issues there, as the internal WebRequest instance is still calling it remotely.
/// Any local files that shouldn't be accessed by this won't be allowed by the remote call.
/// </para>
/// Adapted from <see cref="http://blogengine.codeplex.com">BlogEngine.Net</see>
/// </remarks>
internal sealed class RemoteFile
{
#region Fields
/// <summary>
/// The white-list of urls from which to download remote files.
/// </summary>
private static readonly Uri[] RemoteFileWhiteList = ImageProcessorConfig.Instance.RemoteFileWhiteList;
/// <summary>
/// The length of time, in milliseconds, that a remote file download attempt can last before timing out.
/// </summary>
private static readonly int TimeoutMilliseconds = ImageProcessorConfig.Instance.Timeout;
/// <summary>
/// The maximum size, in bytes, that a remote file download attempt can download.
/// </summary>
private static readonly int MaxBytes = ImageProcessorConfig.Instance.MaxBytes;
/// <summary>
/// Whether to allow remote downloads.
/// </summary>
private static readonly bool AllowRemoteDownloads = ImageProcessorConfig.Instance.AllowRemoteDownloads;
/// <summary>
/// Whether this RemoteFile instance is ignoring remote download rules set in the current application
/// instance.
/// </summary>
private readonly bool ignoreRemoteDownloadSettings;
/// <summary>
/// The <see cref="T:System.Uri">Uri</see> of the remote file being downloaded.
/// </summary>
private readonly Uri url;
/// <summary>
/// The maximum allowable download size in bytes.
/// </summary>
private readonly int maxDownloadSize;
/// <summary>
/// The length of time, in milliseconds, that a remote file download attempt can last before timing out.
/// </summary>
private int timeoutLength;
/// <summary>
/// The <see cref="T:System.Net.WebResponse">WebResponse</see> object used internally for this RemoteFile instance.
/// </summary>
private WebRequest webRequest;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="T:ImageProcessor.Web.Helpers.RemoteFile">RemoteFile</see> class.
/// </summary>
/// <param name="filePath">The url of the file to be downloaded.</param>
/// <param name="ignoreRemoteDownloadSettings">
/// If set to <see langword="true"/>, then RemoteFile should ignore the current the applications instance's remote download settings; otherwise,<see langword="false"/>.
/// </param>
internal RemoteFile(Uri filePath, bool ignoreRemoteDownloadSettings)
{
Contract.Requires(filePath != null);
this.url = filePath;
this.ignoreRemoteDownloadSettings = ignoreRemoteDownloadSettings;
this.timeoutLength = TimeoutMilliseconds;
this.maxDownloadSize = MaxBytes;
}
#endregion
#region Properties
/// <summary>
/// Gets a value indicating whether this RemoteFile instance is ignoring remote download rules set in the
/// current application instance.
/// <remarks>
/// This should only be set to true if the supplied url is a verified resource. Use at your own risk.
/// </remarks>
/// </summary>
/// <value>
/// <see langword="true"/> if this RemoteFile instance is ignoring remote download rules set in the current
/// application instance; otherwise, <see langword="false"/>.
/// </value>
public bool IgnoreRemoteDownloadSettings
{
get
{
return this.ignoreRemoteDownloadSettings;
}
}
/// <summary>
/// Gets the Uri of the remote file being downloaded.
/// </summary>
public Uri Uri
{
get
{
return this.url;
}
}
/// <summary>
/// Gets or sets the length of time, in milliseconds, that a remote file download attempt can
/// last before timing out.
/// <remarks>
/// <para>
/// This value can only be set if the instance is supposed to ignore the remote download settings set
/// in the current application instance.
/// </para>
/// <para>
/// Set this value to 0 if there should be no timeout.
/// </para>
/// </remarks>
/// </summary>
public int TimeoutLength
{
get
{
return this.IgnoreRemoteDownloadSettings ? this.timeoutLength : TimeoutMilliseconds;
}
set
{
if (!this.IgnoreRemoteDownloadSettings)
{
throw new SecurityException("Timeout length can not be adjusted on remote files that are abiding by remote download rules");
}
if (value < 0)
{
throw new ArgumentOutOfRangeException("TimeoutLength");
}
this.timeoutLength = value;
}
}
/// <summary>
/// Gets or sets the maximum download size, in bytes, that a remote file download attempt can be.
/// <remarks>
/// <para>
/// This value can only be set if the instance is supposed to ignore the remote download settings set
/// in the current application instance.
/// </para>
/// <para>
/// Set this value to 0 if there should be no timeout.
/// </para>
/// </remarks>
/// </summary>
public int MaxDownloadSize
{
get
{
return this.IgnoreRemoteDownloadSettings ? this.maxDownloadSize : MaxBytes;
}
set
{
if (!this.IgnoreRemoteDownloadSettings)
{
throw new SecurityException("Max Download Size can not be adjusted on remote files that are abiding by remote download rules");
}
if (value < 0)
{
throw new ArgumentOutOfRangeException("MaxDownloadSize");
}
this.timeoutLength = value;
}
}
#endregion
#region Methods
#region Public
/// <summary>
/// Returns the <see cref="T:System.Net.WebResponse">WebResponse</see> used to download this file.
/// <remarks>
/// <para>
/// This method is meant for outside users who need specific access to the WebResponse this class
/// generates. They're responsible for disposing of it.
/// </para>
/// </remarks>
/// </summary>
/// <returns>The <see cref="T:System.Net.WebResponse">WebResponse</see> used to download this file.</returns>
public WebResponse GetWebResponse()
{
WebResponse response = this.GetWebRequest().GetResponse();
long contentLength = response.ContentLength;
// WebResponse.ContentLength doesn't always know the value, it returns -1 in this case.
if (contentLength == -1)
{
// Response headers may still have the Content-Length inside of it.
string headerContentLength = response.Headers["Content-Length"];
if (!string.IsNullOrWhiteSpace(headerContentLength))
{
contentLength = long.Parse(headerContentLength, CultureInfo.InvariantCulture);
}
}
// We don't need to check the url here since any external urls are available only from the web.config.
if ((this.MaxDownloadSize > 0) && (contentLength > this.MaxDownloadSize))
{
response.Close();
throw new SecurityException("An attempt to download a remote file has been halted because the file is larger than allowed.");
}
return response;
}
/// <summary>
/// Returns the remote file as a String.
/// <remarks>
/// This returns the resulting stream as a string as passed through a StreamReader.
/// </remarks>
/// </summary>
/// <returns>The remote file as a String.</returns>
public string GetFileAsString()
{
using (WebResponse response = this.GetWebResponse())
{
Stream responseStream = response.GetResponseStream();
if (responseStream != null)
{
// Pipe the stream to a stream reader with the required encoding format.
using (StreamReader reader = new StreamReader(responseStream, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
return string.Empty;
}
}
#endregion
#region Private
/// <summary>
/// Performs a check to see whether the application is able to download remote files.
/// </summary>
private void CheckCanDownload()
{
if (!this.IgnoreRemoteDownloadSettings && !AllowRemoteDownloads)
{
throw new SecurityException("application is not configured to allow remote file downloads.");
}
}
/// <summary>
/// Creates the WebRequest object used internally for this RemoteFile instance.
/// </summary>
/// <returns>
/// <para>
/// The WebRequest should not be passed outside of this instance, as it will allow tampering. Anyone
/// that needs more fine control over the downloading process should probably be using the WebRequest
/// class on its own.
/// </para>
/// </returns>
private WebRequest GetWebRequest()
{
// Check downloads are allowed.
this.CheckCanDownload();
// Check the url is from a whitelisted location.
this.CheckSafeUrlLocation();
if (this.webRequest == null)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(this.Uri);
request.Headers["Accept-Encoding"] = "gzip";
request.Headers["Accept-Language"] = "en-us";
request.Credentials = CredentialCache.DefaultNetworkCredentials;
request.AutomaticDecompression = DecompressionMethods.GZip;
if (this.TimeoutLength > 0)
{
request.Timeout = this.TimeoutLength;
}
this.webRequest = request;
}
return this.webRequest;
}
/// <summary>
/// Returns a value indicating whether the current url is in a list of safe download locations.
/// </summary>
private void CheckSafeUrlLocation()
{
bool validUrl = RemoteFileWhiteList.Any(item => item.Host.ToUpperInvariant().Equals(this.url.Host.ToUpperInvariant()));
if (!validUrl)
{
throw new SecurityException("application is not configured to allow remote file downloads from this domain.");
}
}
#endregion
#endregion
}
}

252
src/ImageProcessor.Web/HttpModules/ImageProcessingModule.cs

@ -0,0 +1,252 @@
// -----------------------------------------------------------------------
// <copyright file="ImageProcessingModule.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Web.HttpModules
{
#region Using
using System;
using System.IO;
using System.Net;
using System.Web;
using System.Web.Hosting;
using ImageProcessor.Helpers.Extensions;
using ImageProcessor.Imaging;
using ImageProcessor.Web.Caching;
using ImageProcessor.Web.Config;
using ImageProcessor.Web.Helpers;
#endregion
/// <summary>
/// TODO: Update summary.
/// </summary>
public class ImageProcessingModule : IHttpModule
{
#region Fields
/// <summary>
/// The value to prefix any remote image requests with to ensure they get captured.
/// </summary>
private static readonly string RemotePrefix = ImageProcessorConfig.Instance.RemotePrefix;
/// <summary>
/// The key for storing the response type of the current image.
/// </summary>
private const string CachedResponseTypeKey = "CACHED_IMAGE_RESPONSE_TYPE";
/// <summary>
/// Whether this is the first run of the handler.
/// </summary>
private static bool isFirstRun = true;
/// <summary>
/// A counter for keeping track of how many images have been added to the cache.
/// </summary>
private static int cachedImageCounter;
#endregion
#region IHttpModule Members
/// <summary>
/// Initializes a module and prepares it to handle requests.
/// </summary>
/// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application </param>
public void Init(HttpApplication context)
{
context.BeginRequest += this.ContextBeginRequest;
context.PreSendRequestHeaders += this.ContextPreSendRequestHeaders;
}
/// <summary>
/// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
/// </summary>
public void Dispose()
{
// Nothing to dispose.
}
#endregion
/// <summary>
/// Occurs as the first event in the HTTP pipeline chain of execution when ASP.NET responds to a request.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">An <see cref="T:System.EventArgs">EventArgs</see> that contains the event data.</param>
private void ContextBeginRequest(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
// Is this a remote file.
bool isRemote = context.Request.Path.Equals(RemotePrefix, StringComparison.OrdinalIgnoreCase);
string path;
string queryString = string.Empty;
if (isRemote)
{
// We need to split the querystring to get the actual values we want.
string[] paths = HttpUtility.UrlDecode(context.Request.QueryString.ToString()).Split('?');
path = paths[0];
if (paths.Length > 1)
{
queryString = paths[1];
}
}
else
{
path = HostingEnvironment.MapPath(context.Request.Path);
queryString = context.Request.QueryString.ToString();
}
if (ImageUtils.IsValidImageExtension(path) && !string.IsNullOrWhiteSpace(queryString))
{
string fullPath = string.Format("{0}?{1}", path, queryString);
string imageName = Path.GetFileName(path);
string cachedPath = DiskCache.GetCachePath(fullPath, imageName);
if (path != null && this.FileExists(path, isRemote))
{
bool exists = File.Exists(cachedPath);
bool updated = DiskCache.IsUpdatedFile(path, cachedPath);
if ((exists == false) || (!isRemote && updated))
{
// Check to see if this is the first run and if so run the cache controller.
if (isFirstRun)
{
// Trim the cache.
DiskCache.PurgeCachedFolders();
// Disable the controller.
isFirstRun = false;
}
// ImageFactory.Instance.Load(fullPath).AutoProcess().Save(cachedPath);
using (ImageFactory imageFactory = new ImageFactory())
{
if (isRemote)
{
Uri uri = new Uri(path);
RemoteFile remoteFile = new RemoteFile(uri, false);
using (MemoryStream memoryStream = new MemoryStream())
{
using (Stream responseStream = remoteFile.GetWebResponse().GetResponseStream())
{
if (responseStream != null)
{
responseStream.CopyTo(memoryStream);
imageFactory.Load(memoryStream)
.AddQueryString(queryString)
.Format(ImageUtils.GetImageFormat(imageName))
.AutoProcess().Save(cachedPath);
}
}
}
}
else
{
imageFactory.Load(fullPath).AutoProcess().Save(cachedPath);
}
}
// Add 1 to the counter
cachedImageCounter += 1;
// Ensure that the LastWriteTime property of the source and cached file match.
DiskCache.SetCachedLastWriteTime(path, cachedPath);
// If the number of cached imaged hits the maximum allowed for this session then we clear
// the cache again and reset the counter
if (cachedImageCounter >= DiskCache.MaxRunsBeforeCacheClear)
{
DiskCache.PurgeCachedFolders();
cachedImageCounter = 0;
}
}
context.Items[CachedResponseTypeKey] = ImageUtils.GetResponseType(imageName).ToDescription();
// The cached file is valid so just rewrite the path.
context.RewritePath(DiskCache.GetVirtualPath(cachedPath, context.Request), false);
}
}
}
/// <summary>
/// Occurs just before ASP.NET send Httpheaders to the client.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">An <see cref="T:System.EventArgs">EventArgs</see> that contains the event data.</param>
private void ContextPreSendRequestHeaders(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
object responseTypeObject = context.Items[CachedResponseTypeKey];
if (responseTypeObject != null)
{
string responseType = (string)responseTypeObject;
this.SetHeaders(context, responseType);
context.Items[CachedResponseTypeKey] = null;
}
}
#region Private
/// <summary>
/// returns a value indicating whether a file exists.
/// </summary>
/// <param name="path">The path to the file to check.</param>
/// <param name="remote">Whether the file is remote.</param>
/// <returns>True if the file exists, otherwise false.</returns>
/// <remarks>If the file is remote the method will always return true.</remarks>
private bool FileExists(string path, bool remote)
{
return remote || File.Exists(path);
}
/// <summary>
/// This will make the browser and server keep the output
/// in its cache and thereby improve performance.
/// See http://en.wikipedia.org/wiki/HTTP_ETag
/// </summary>
/// <param name="context">
/// the <see cref="T:System.Web.HttpContext">HttpContext</see> object that provides
/// references to the intrinsic server objects
/// </param>
/// <param name="responseType">The HTTP MIME type to to send.</param>
private void SetHeaders(HttpContext context, string responseType)
{
HttpResponse response = context.Response;
response.ContentType = responseType;
HttpCachePolicy cache = response.Cache;
cache.VaryByHeaders["Accept-Encoding"] = true;
int maxDays = DiskCache.MaxFileCachedDuration;
cache.SetExpires(DateTime.Now.ToUniversalTime().AddDays(maxDays));
cache.SetMaxAge(new TimeSpan(maxDays, 0, 0, 0));
cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
string incomingEtag = context.Request.Headers["If-None-Match"];
cache.SetCacheability(HttpCacheability.Public);
if (incomingEtag == null)
{
return;
}
response.Clear();
response.StatusCode = (int)HttpStatusCode.NotModified;
response.SuppressContent = true;
}
#endregion
}
}

50
src/ImageProcessor.Web/ImageFactoryExtensions.cs

@ -0,0 +1,50 @@
// -----------------------------------------------------------------------
// <copyright file="ImageFactoryExtensions.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Web
{
#region Using
using System.Collections.Generic;
using System.Linq;
using ImageProcessor.Processors;
using ImageProcessor.Web.Config;
#endregion
/// <summary>
/// Extends the ImageFactory class to provide a fluent api.
/// </summary>
public static class ImageFactoryExtensions
{
/// <summary>
/// Auto processes image files based on any querystring parameters added to the image path.
/// </summary>
/// <param name="factory">
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class
/// that this method extends.
/// </param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public static ImageFactory AutoProcess(this ImageFactory factory)
{
if (factory.ShouldProcess)
{
// Get a list of all graphics processors that have parsed and matched the querystring.
List<IGraphicsProcessor> list =
ImageProcessorConfig.Instance.GraphicsProcessors.Where(x => x.MatchRegexIndex(factory.QueryString) != int.MaxValue).OrderBy(
y => y.SortOrder).ToList();
// Loop through and process the image.
foreach (IGraphicsProcessor graphicsProcessor in list)
{
factory.Image = graphicsProcessor.ProcessImage(factory);
}
}
return factory;
}
}
}

80
src/ImageProcessor.Web/ImageProcessor.Web.csproj

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{4F7050F2-465F-4E10-8DB2-2FB97AC6AA43}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ImageProcessor.Web</RootNamespace>
<AssemblyName>ImageProcessor.Web</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'All|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\All\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Caching\DiskCache.cs" />
<Compile Include="Config\ImageCacheSection.cs" />
<Compile Include="Config\ImageProcessingSection.cs" />
<Compile Include="Config\ImageProcessorConfig.cs" />
<Compile Include="Config\ImageSecuritySection.cs" />
<Compile Include="Helpers\FileCompareLastwritetime.cs" />
<Compile Include="Helpers\RemoteFile.cs" />
<Compile Include="HttpModules\ImageProcessingModule.cs" />
<Compile Include="ImageFactoryExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ImageProcessor\ImageProcessor.csproj">
<Project>{3B5DD734-FB7A-487D-8CE6-55E7AF9AEA7E}</Project>
<Name>ImageProcessor</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

96
src/ImageProcessor.Web/ImageProcessor.Web.vsdoc

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- VSdocman config file for current project/solution.-->
<activeProfile>default</activeProfile>
<appSettings>
<add key="VBdocman_regexFilters"><![CDATA[<?xml version="1.0" encoding="utf-8"?>
<filters />]]></add>
<add key="VBdocman_comNonCommented"><![CDATA[-1]]></add>
<add key="VBdocman_comPublic"><![CDATA[-1]]></add>
<add key="VBdocman_comPrivate"><![CDATA[0]]></add>
<add key="VBdocman_comFriend"><![CDATA[0]]></add>
<add key="VBdocman_comProtected"><![CDATA[0]]></add>
<add key="VBdocman_comProtectedFriend"><![CDATA[0]]></add>
<add key="VBdocman_comMethod"><![CDATA[-1]]></add>
<add key="VBdocman_comStdModule"><![CDATA[0]]></add>
<add key="VBdocman_comObject"><![CDATA[-1]]></add>
<add key="VBdocman_comForm"><![CDATA[-1]]></add>
<add key="VBdocman_comProperty"><![CDATA[-1]]></add>
<add key="VBdocman_comEvent"><![CDATA[-1]]></add>
<add key="VBdocman_comVariable"><![CDATA[0]]></add>
<add key="VBdocman_comConstant"><![CDATA[0]]></add>
<add key="VBdocman_comEnumeration"><![CDATA[0]]></add>
<add key="VBdocman_comStructure"><![CDATA[0]]></add>
<add key="VBdocman_comDelegate"><![CDATA[0]]></add>
<add key="VBdocman_comInterface"><![CDATA[0]]></add>
<add key="VBdocman_comAttribute"><![CDATA[0]]></add>
<add key="VBdocman_comEventDecl"><![CDATA[0]]></add>
<add key="VBdocman_comDeclare"><![CDATA[0]]></add>
<add key="VBdocman_comContextID"><![CDATA[0]]></add>
<add key="VBdocman_comWriteDescription"><![CDATA[-1]]></add>
<add key="VBdocman_useConditionalCompilation"><![CDATA[0]]></add>
<add key="VBdocman_conditionalConstants"><![CDATA[]]></add>
<add key="VBdocman_showFormsSeparate"><![CDATA[0]]></add>
<add key="VBdocman_showInherited"><![CDATA[-1]]></add>
<add key="VBdocman_indentMode"><![CDATA[0]]></add>
<add key="VBdocman_insertSourceGlobal"><![CDATA[0]]></add>
<add key="VBdocman_unbreakSourceLines"><![CDATA[0]]></add>
<add key="VBdocman_removeAttributes"><![CDATA[0]]></add>
<add key="VBdocman_generateVbSyntax"><![CDATA[-1]]></add>
<add key="VBdocman_generateCsharpSyntax"><![CDATA[-1]]></add>
<add key="VBdocman_generateCppSyntax"><![CDATA[-1]]></add>
<add key="VBdocman_generateJscriptSyntax"><![CDATA[-1]]></add>
<add key="VBdocman_supportedPlatforms"><![CDATA[Windows 98, Windows 2000 SP4, Windows Millennium Edition, Windows Server 2003, Windows XP Media Center Edition, Windows XP Professional x64 Edition, Windows XP SP2, Windows XP Starter Edition]]></add>
<add key="VBdocman_supportedNetFramework"><![CDATA[3.0, 2.0, 1.1, 1.0]]></add>
<add key="VBdocman_supportedNetCompactFramework"><![CDATA[2.0, 1.0]]></add>
<add key="VBdocman_supportedXnaFramework"><![CDATA[1.0]]></add>
<add key="VBdocman_customVar1"><![CDATA[]]></add>
<add key="VBdocman_customVar2"><![CDATA[]]></add>
<add key="VBdocman_customVar3"><![CDATA[]]></add>
<add key="VBdocman_titlePageText"><![CDATA[]]></add>
<add key="VBdocman_rootNamespaceText"><![CDATA[]]></add>
<add key="VBdocman_rootNamespaceCommentStyle"><![CDATA[2]]></add>
<add key="VBdocman_pageFooterText"><![CDATA[Generated by VSdocman]]></add>
<add key="VBdocman_outputPath"><![CDATA[$(ProjectDir)VSdoc]]></add>
<add key="VBdocman_templateFolder"><![CDATA[$(VSdocmanDir)Templates]]></add>
<add key="VBdocman_externalFilesFolder"><![CDATA[]]></add>
<add key="VBdocman_fileNamingConvention"><![CDATA[1]]></add>
<add key="VBdocman_templateLocale"><![CDATA[en-US]]></add>
<add key="VBdocman_templatePath"><![CDATA[chm_msdn2.vbdt]]></add>
<add key="VBdocman_enumSorting"><![CDATA[1]]></add>
<add key="VBdocman_helpTitle"><![CDATA[ImageProcessor.Web Reference]]></add>
<add key="VBdocman_customTopics"><![CDATA[<?xml version="1.0" encoding="utf-8"?>
<topics>
<topic>
<type>normal</type>
<is-default>yes</is-default>
<name>ImageProcessor.Web Reference</name>
<id>imageprocessorweb_reference</id>
<comment><![CDATA[<summary></summary>vsdocman_escaped_]_]_></comment>
<namespaces />
<topics>
<topic>
<type>placeholder</type>
<is-default>no</is-default>
<name />
<id>d85a6fde0cd5454f9dc418dda28f5422</id>
<comment><![CDATA[vsdocman_escaped_]_]_></comment>
<namespaces />
<topics />
</topic>
</topics>
</topic>
</topics>]]></add>
<add key="VBdocman_linkForExternalNotInFramework"><![CDATA[0]]></add>
<add key="VBdocman_allowMacrosInComments"><![CDATA[0]]></add>
<add key="VBdocman_emptyOutputFolder"><![CDATA[0]]></add>
<add key="VBdocman_comModules_Caching...DiskCache.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Config...ImageCacheSection.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Config...ImageProcessingSection.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Config...ImageProcessorConfig.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Config...ImageSecuritySection.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Helpers...RemoteFile.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_HttpModules...ImageProcessingModule.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Properties...AssemblyInfo.cs"><![CDATA[On]]></add>
</appSettings>
</configuration>

36
src/ImageProcessor.Web/Properties/AssemblyInfo.cs

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ImageProcessor.Web")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ImageProcessor.Web")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("cef6713b-4088-488a-ad2c-6f94aff082d5")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

40
src/ImageProcessor/Helpers/Extensions/EnumExtensions.cs

@ -0,0 +1,40 @@
// -----------------------------------------------------------------------
// <copyright file="EnumExtensions.cs" company="Peach James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Helpers.Extensions
{
#region Using
using System;
using System.ComponentModel;
using System.Diagnostics.Contracts;
#endregion
/// <summary>
/// Encapsulates a series of time saving extension methods to <see cref="T:System.Enum">Enum</see>s.
/// </summary>
public static class EnumExtensions
{
#region Methods
/// <summary>
/// Extends the <see cref="T:System.Enum">Enum</see> type to return the description attribute for the given type.
/// Useful for when the type to match in the data source contains spaces.
/// </summary>
/// <param name="expression">The given <see cref="T:System.Enum">Enum</see> that this method extends.</param>
/// <returns>A string containing the Enum's description attribute.</returns>
public static string ToDescription(this Enum expression)
{
Contract.Requires(expression != null);
DescriptionAttribute[] descriptionAttribute =
(DescriptionAttribute[])
expression.GetType().GetField(expression.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false);
return descriptionAttribute.Length > 0 ? descriptionAttribute[0].Description : expression.ToString();
}
#endregion
}
}

116
src/ImageProcessor/Helpers/Extensions/StringExtensions.cs

@ -0,0 +1,116 @@
// -----------------------------------------------------------------------
// <copyright file="StringExtensions.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Helpers.Extensions
{
#region Using
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
#endregion
/// <summary>
/// Encapsulates a series of time saving extension methods to <see cref="T:System.String">String</see>s.
/// </summary>
public static class StringExtensions
{
#region Cryptography
/// <summary>
/// Creates an MD5 fingerprint of the String.
/// </summary>
/// <param name="expression">The <see cref="T:System.String">String</see> instance that this method extends.</param>
/// <returns>An MD5 fingerprint of the String.</returns>
public static string ToMD5Fingerprint(this string expression)
{
Contract.Requires(!string.IsNullOrWhiteSpace(expression));
byte[] bytes = Encoding.Unicode.GetBytes(expression.ToCharArray());
using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
{
byte[] hash = md5.ComputeHash(bytes);
// Concatenate the hash bytes into one long String.
return hash.Aggregate(
new StringBuilder(32),
(sb, b) => sb.Append(b.ToString("X2", CultureInfo.InvariantCulture)))
.ToString();
}
}
#endregion
#region Numbers
/// <summary>
/// Creates an array of integers scraped from the String.
/// </summary>
/// <param name="expression">The <see cref="T:System.String">String</see> instance that this method extends.</param>
/// <returns>An array of integers scraped from the String.</returns>
public static int[] ToIntegerArray(this string expression)
{
Contract.Requires(!string.IsNullOrWhiteSpace(expression));
Regex regex = new Regex(@"\d+", RegexOptions.Compiled);
MatchCollection matchCollection = regex.Matches(expression);
// Get the collections.
int count = matchCollection.Count;
int[] matches = new int[count];
// Loop and parse the int values.
for (int i = 0; i < count; i++)
{
matches[i] = int.Parse(matchCollection[i].Value);
}
return matches;
}
#endregion
#region Files and Paths
/// <summary>
/// Checks the string to see whether the value is a valid virtual path name.
/// </summary>
/// <param name="expression">The <see cref="T:System.String">String</see> instance that this method extends.</param>
/// <returns>True if the given string is a valid virtual path name</returns>
public static bool IsValidVirtualPathName(this string expression)
{
Contract.Requires(!string.IsNullOrWhiteSpace(expression));
// Check the start of the string.
if (expression.StartsWith("~/"))
{
// Trim the first two characters and test the path.
expression = expression.Substring(2);
return expression.IsValidPathName();
}
return false;
}
/// <summary>
/// Checks the string to see whether the value is a valid path name.
/// http://stackoverflow.com/questions/62771/how-check-if-given-string-is-legal-allowed-file-name-under-windows/
/// </summary>
/// <param name="expression">The <see cref="T:System.String">String</see> instance that this method extends.</param>
/// <returns>True if the given string is a valid path name</returns>
public static bool IsValidPathName(this string expression)
{
Contract.Requires(!string.IsNullOrWhiteSpace(expression));
// Create a regex of invalid characters and test it.
string invalidPathNameChars = new string(Path.GetInvalidFileNameChars());
Regex regFixPathName = new Regex("[" + Regex.Escape(invalidPathNameChars) + "]");
return !regFixPathName.IsMatch(expression);
}
#endregion
}
}

498
src/ImageProcessor/ImageFactory.cs

@ -0,0 +1,498 @@
// -----------------------------------------------------------------------
// <copyright file="ImageFactory.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor
{
#region Using
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using ImageProcessor.Imaging;
using ImageProcessor.Processors;
#endregion
/// <summary>
/// Encapsulates methods for processing image files.
/// http://csharpindepth.com/Articles/General/Singleton.aspx
/// </summary>
public class ImageFactory : IDisposable
{
#region Fields
/// <summary>
/// The default quality for jpeg files.
/// </summary>
private const int DefaultJpegQuality = 90;
/// <summary>
/// A value indicating whether this instance of the given entity has been disposed.
/// </summary>
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
/// <remarks>
/// If the entity is disposed, it must not be disposed a second
/// time. The isDisposed field is set the first time the entity
/// is disposed. If the isDisposed field is true, then the Dispose()
/// method will not dispose again. This help not to prolong the entity's
/// life in the Garbage Collector.
/// </remarks>
private bool isDisposed;
#endregion
#region Destructors
/// <summary>
/// Finalizes an instance of the <see cref="T:ImageProcessor.ImageFactory">ImageFactory</see> class.
/// </summary>
/// <remarks>
/// Use C# destructor syntax for finalization code.
/// This destructor will run only if the Dispose method
/// does not get called.
/// It gives your base class the opportunity to finalize.
/// Do not provide destructors in types derived from this class.
/// </remarks>
~ImageFactory()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
this.Dispose(false);
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the local image for manipulation.
/// </summary>
public Image Image { get; set; }
/// <summary>
/// Gets the path to the local image for manipulation.
/// </summary>
public string ImagePath { get; private set; }
/// <summary>
/// Gets the querystring params for web image manipulation.
/// </summary>
public string QueryString { get; private set; }
/// <summary>
/// Gets a value indicating whether the image factory should process the file.
/// </summary>
public bool ShouldProcess { get; private set; }
/// <summary>
/// Gets or sets the quality of output for jpeg images as a percentile.
/// </summary>
internal int JpegQuality { get; set; }
/// <summary>
/// Gets or sets the file format of the image.
/// </summary>
internal ImageFormat ImageFormat { get; set; }
#endregion
#region Methods
/// <summary>
/// Loads the image to process. Always call this method first.
/// </summary>
/// <param name="memoryStream">
/// The <see cref="T:System.IO.MemoryStream"/> containing the image information.
/// </param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Load(MemoryStream memoryStream)
{
// Set our image as the memorystream value.
this.Image = Image.FromStream(memoryStream);
// Store the stream in the image Tag property so we can dispose of it later.
this.Image.Tag = memoryStream;
// Set the other properties.
this.JpegQuality = DefaultJpegQuality;
this.ImageFormat = ImageFormat.Jpeg;
this.ShouldProcess = true;
return this;
}
/// <summary>
/// Loads the image to process. Always call this method first.
/// </summary>
/// <param name="imagePath">The absolute path to the image to load.</param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Load(string imagePath)
{
string[] paths = imagePath.Split('?');
string path = paths[0];
string query = string.Empty;
if (paths.Length > 1)
{
query = paths[1];
}
string imageName = Path.GetFileName(path);
if (File.Exists(path))
{
this.ImagePath = path;
this.QueryString = query;
// Open a filstream to prevent the need for lock.
using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
MemoryStream memoryStream = new MemoryStream();
// Copy the stream.
fileStream.CopyTo(memoryStream);
// Set the position to 0 afterwards.
fileStream.Position = memoryStream.Position = 0;
// Set our image as the memorystream value.
this.Image = Image.FromStream(memoryStream);
// Store the stream in the image Tag property so we can dispose of it later.
this.Image.Tag = memoryStream;
// Set the other properties.
this.JpegQuality = DefaultJpegQuality;
this.ImageFormat = ImageUtils.GetImageFormat(imageName);
this.ShouldProcess = true;
}
}
return this;
}
#region Manipulation
/// <summary>
/// Adds a querystring to the image factory to allow autoprocessing of remote files.
/// </summary>
/// <param name="query">The querystring parameter to process.</param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory AddQueryString(string query)
{
if (this.ShouldProcess)
{
this.QueryString = query;
}
return this;
}
/// <summary>
/// Changes the opacity of the current image.
/// </summary>
/// <param name="percentage">The percentage by which to alter the images opacity.</param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Alpha(int percentage)
{
if (this.ShouldProcess)
{
var alpha = new Alpha { DynamicParameter = percentage };
this.Image = alpha.ProcessImage(this);
}
return this;
}
/// <summary>
/// Crops an image to the given coordinates.
/// </summary>
/// <param name="rectangle">
/// The <see cref="T:System.Drawing.Rectangle"/> containing the coordinates to crop the image to.
/// </param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Crop(Rectangle rectangle)
{
if (this.ShouldProcess)
{
var crop = new Crop { DynamicParameter = rectangle };
this.Image = crop.ProcessImage(this);
}
return this;
}
/// <summary>
/// Applies a filter to an image.
/// </summary>
/// <param name="filterName">
/// The name of the filter to add to the image.
/// </param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Filter(string filterName)
{
if (this.ShouldProcess)
{
var filter = new Filter { DynamicParameter = filterName };
this.Image = filter.ProcessImage(this);
}
return this;
}
/// <summary>
/// Sets the output format of the image to the matching <see cref="T:System.Drawing.Imaging.ImageFormat"/>.
/// </summary>
/// <param name="imageFormat">The <see cref="T:System.Drawing.Imaging.ImageFormat"/>. to set the image to.</param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Format(ImageFormat imageFormat)
{
if (this.ShouldProcess)
{
this.ImageFormat = imageFormat;
}
return this;
}
/// <summary>
/// Applies a filter to an image.
/// </summary>
/// <param name="percentage">A value between 1 and 100 to set the quality to.</param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Quality(int percentage)
{
if (this.ShouldProcess)
{
this.JpegQuality = percentage;
}
return this;
}
/// <summary>
/// Resizes an image to the given dimensions.
/// </summary>
/// <param name="width">The width to set the image to.</param>
/// <param name="height">The height to set the image to.</param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Resize(int width, int height)
{
if (this.ShouldProcess)
{
var resizeSettings = new Dictionary<string, string> { { "MaxWidth", width.ToString("G") }, { "MaxHeight", height.ToString("G") } };
var resize = new Resize { DynamicParameter = new Size(width, height), Settings = resizeSettings };
this.Image = resize.ProcessImage(this);
}
return this;
}
/// <summary>
/// Adds a vignette image effect to the current image.
/// </summary>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Vignette()
{
if (this.ShouldProcess)
{
var vignette = new Vignette();
this.Image = vignette.ProcessImage(this);
}
return this;
}
#endregion
/// <summary>
/// Saves the current image to the specified file path.
/// </summary>
/// <param name="filePath">The path to save the image to.</param>
public void Save(string filePath)
{
if (this.ShouldProcess)
{
// Fix the colour palette of gif images.
this.FixGifs();
if (this.ImageFormat == ImageFormat.Jpeg)
{
// Jpegs can be saved with different settings to include a quality setting for the JPEG compression.
// This improves output compression and quality.
using (EncoderParameters encoderParameters = ImageUtils.GetEncodingParameters(this.JpegQuality))
{
ImageCodecInfo imageCodecInfo =
ImageCodecInfo.GetImageEncoders().FirstOrDefault(
ici => ici.MimeType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase));
this.Image.Save(filePath, imageCodecInfo, encoderParameters);
}
}
else
{
this.Image.Save(filePath, this.ImageFormat);
}
}
}
/// <summary>
/// Saves the current image to the specified output stream.
/// </summary>
/// <param name="memoryStream">
/// The <see cref="T:System.IO.MemoryStream"/> to save the image information to.
/// </param>
public void Save(MemoryStream memoryStream)
{
if (this.ShouldProcess)
{
// Fix the colour palette of gif images.
this.FixGifs();
if (this.ImageFormat == ImageFormat.Jpeg)
{
// Jpegs can be saved with different settings to include a quality setting for the JPEG compression.
// This improves output compression and quality.
using (EncoderParameters encoderParameters = ImageUtils.GetEncodingParameters(this.JpegQuality))
{
ImageCodecInfo imageCodecInfo =
ImageCodecInfo.GetImageEncoders().FirstOrDefault(
ici => ici.MimeType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase));
if (imageCodecInfo != null)
{
this.Image.Save(memoryStream, imageCodecInfo, encoderParameters);
}
}
}
else
{
this.Image.Save(memoryStream, this.ImageFormat);
}
}
}
#region IDisposable Members
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
public void Dispose()
{
this.Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
/// <param name="disposing">If true, the object gets disposed.</param>
protected virtual void Dispose(bool disposing)
{
if (this.isDisposed)
{
return;
}
if (disposing)
{
// Dispose of any managed resources here.
if (this.Image != null)
{
// Dispose of the memorystream from Load and the image.
if (this.Image.Tag != null)
{
((IDisposable)this.Image.Tag).Dispose();
this.Image.Tag = null;
}
this.Image.Dispose();
this.Image = null;
}
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// Note disposing is done.
this.isDisposed = true;
}
#endregion
/// <summary>
/// Saves the current image to the specified file path and resets any internal parameters.
/// </summary>
/// <param name="filePath">The path to save the image to.</param>
private void SaveFile(string filePath)
{
// Fix the colour palette of gif images.
this.FixGifs();
if (this.ImageFormat == ImageFormat.Jpeg)
{
// Jpegs can be saved with different settings to include a quality setting for the JPEG compression.
// This improves output compression and quality.
using (EncoderParameters encoderParameters = ImageUtils.GetEncodingParameters(this.JpegQuality))
{
ImageCodecInfo imageCodecInfo = ImageCodecInfo.GetImageEncoders()
.FirstOrDefault(ici => ici.MimeType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase));
if (imageCodecInfo != null)
{
this.Image.Save(filePath, imageCodecInfo, encoderParameters);
}
}
}
else
{
this.Image.Save(filePath, this.ImageFormat);
}
}
/// <summary>
/// Uses the <see cref="T:ImageProcessor.Imaging.OctreeQuantizer"/>
/// to fix the colour palette of gif images.
/// </summary>
private void FixGifs()
{
// Fix the colour palette of gif images.
// TODO: Why does the palette not get fixed when resized to the same dimensions.
if (this.ImageFormat == ImageFormat.Gif)
{
OctreeQuantizer quantizer = new OctreeQuantizer(255, 8);
this.Image = quantizer.Quantize(this.Image);
}
}
#endregion
}
}

86
src/ImageProcessor/ImageProcessor.csproj

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{3B5DD734-FB7A-487D-8CE6-55E7AF9AEA7E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ImageProcessor</RootNamespace>
<AssemblyName>ImageProcessor</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\ImageProcessor.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\ImageProcessor.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'All|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\All\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\ImageProcessor.XML</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisIgnoreBuiltInRuleSets>false</CodeAnalysisIgnoreBuiltInRuleSets>
<CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Helpers\Extensions\EnumExtensions.cs" />
<Compile Include="Helpers\Extensions\StringExtensions.cs" />
<Compile Include="ImageFactory.cs" />
<Compile Include="Imaging\PaletteQuantizer.cs" />
<Compile Include="Imaging\ImageUtils.cs" />
<Compile Include="Imaging\OctreeQuantizer.cs" />
<Compile Include="Imaging\Quantizer.cs" />
<Compile Include="Imaging\ResponseType.cs" />
<Compile Include="Processors\Alpha.cs" />
<Compile Include="Processors\Crop.cs" />
<Compile Include="Processors\Filter.cs" />
<Compile Include="Processors\IGraphicsProcessor.cs" />
<Compile Include="Processors\Quality.cs" />
<Compile Include="Processors\Resize.cs" />
<Compile Include="Processors\Format.cs" />
<Compile Include="Processors\Vignette.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

39
src/ImageProcessor/ImageProcessor.sln

@ -0,0 +1,39 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessor", "ImageProcessor.csproj", "{3B5DD734-FB7A-487D-8CE6-55E7AF9AEA7E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "..\Test\Test\Test.csproj", "{30327C08-7574-4D7E-AC95-6A58753C6855}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessor.Web", "..\ImageProcessor.Web\ImageProcessor.Web.csproj", "{4F7050F2-465F-4E10-8DB2-2FB97AC6AA43}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
All|Any CPU = All|Any CPU
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3B5DD734-FB7A-487D-8CE6-55E7AF9AEA7E}.All|Any CPU.ActiveCfg = All|Any CPU
{3B5DD734-FB7A-487D-8CE6-55E7AF9AEA7E}.All|Any CPU.Build.0 = All|Any CPU
{3B5DD734-FB7A-487D-8CE6-55E7AF9AEA7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B5DD734-FB7A-487D-8CE6-55E7AF9AEA7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B5DD734-FB7A-487D-8CE6-55E7AF9AEA7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B5DD734-FB7A-487D-8CE6-55E7AF9AEA7E}.Release|Any CPU.Build.0 = Release|Any CPU
{30327C08-7574-4D7E-AC95-6A58753C6855}.All|Any CPU.ActiveCfg = All|Any CPU
{30327C08-7574-4D7E-AC95-6A58753C6855}.All|Any CPU.Build.0 = All|Any CPU
{30327C08-7574-4D7E-AC95-6A58753C6855}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{30327C08-7574-4D7E-AC95-6A58753C6855}.Debug|Any CPU.Build.0 = Debug|Any CPU
{30327C08-7574-4D7E-AC95-6A58753C6855}.Release|Any CPU.ActiveCfg = Release|Any CPU
{30327C08-7574-4D7E-AC95-6A58753C6855}.Release|Any CPU.Build.0 = Release|Any CPU
{4F7050F2-465F-4E10-8DB2-2FB97AC6AA43}.All|Any CPU.ActiveCfg = All|Any CPU
{4F7050F2-465F-4E10-8DB2-2FB97AC6AA43}.All|Any CPU.Build.0 = All|Any CPU
{4F7050F2-465F-4E10-8DB2-2FB97AC6AA43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F7050F2-465F-4E10-8DB2-2FB97AC6AA43}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F7050F2-465F-4E10-8DB2-2FB97AC6AA43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F7050F2-465F-4E10-8DB2-2FB97AC6AA43}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

7
src/ImageProcessor/ImageProcessor.sln.vsdoc

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- VSdocman config file for current project/solution.-->
<activeProfile>default</activeProfile>
<appSettings>
</appSettings>
</configuration>

103
src/ImageProcessor/ImageProcessor.vsdoc

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- VSdocman config file for current project/solution.-->
<activeProfile>default</activeProfile>
<appSettings>
<add key="VBdocman_regexFilters"><![CDATA[<?xml version="1.0" encoding="utf-8"?>
<filters />]]></add>
<add key="VBdocman_comNonCommented"><![CDATA[-1]]></add>
<add key="VBdocman_comPublic"><![CDATA[-1]]></add>
<add key="VBdocman_comPrivate"><![CDATA[0]]></add>
<add key="VBdocman_comFriend"><![CDATA[0]]></add>
<add key="VBdocman_comProtected"><![CDATA[0]]></add>
<add key="VBdocman_comProtectedFriend"><![CDATA[0]]></add>
<add key="VBdocman_comMethod"><![CDATA[-1]]></add>
<add key="VBdocman_comStdModule"><![CDATA[0]]></add>
<add key="VBdocman_comObject"><![CDATA[-1]]></add>
<add key="VBdocman_comForm"><![CDATA[-1]]></add>
<add key="VBdocman_comProperty"><![CDATA[-1]]></add>
<add key="VBdocman_comEvent"><![CDATA[-1]]></add>
<add key="VBdocman_comVariable"><![CDATA[0]]></add>
<add key="VBdocman_comConstant"><![CDATA[0]]></add>
<add key="VBdocman_comEnumeration"><![CDATA[0]]></add>
<add key="VBdocman_comStructure"><![CDATA[0]]></add>
<add key="VBdocman_comDelegate"><![CDATA[0]]></add>
<add key="VBdocman_comInterface"><![CDATA[0]]></add>
<add key="VBdocman_comAttribute"><![CDATA[0]]></add>
<add key="VBdocman_comEventDecl"><![CDATA[0]]></add>
<add key="VBdocman_comDeclare"><![CDATA[0]]></add>
<add key="VBdocman_comContextID"><![CDATA[0]]></add>
<add key="VBdocman_comWriteDescription"><![CDATA[-1]]></add>
<add key="VBdocman_useConditionalCompilation"><![CDATA[0]]></add>
<add key="VBdocman_conditionalConstants"><![CDATA[]]></add>
<add key="VBdocman_showFormsSeparate"><![CDATA[0]]></add>
<add key="VBdocman_showInherited"><![CDATA[-1]]></add>
<add key="VBdocman_indentMode"><![CDATA[0]]></add>
<add key="VBdocman_insertSourceGlobal"><![CDATA[0]]></add>
<add key="VBdocman_unbreakSourceLines"><![CDATA[0]]></add>
<add key="VBdocman_removeAttributes"><![CDATA[0]]></add>
<add key="VBdocman_generateVbSyntax"><![CDATA[-1]]></add>
<add key="VBdocman_generateCsharpSyntax"><![CDATA[-1]]></add>
<add key="VBdocman_generateCppSyntax"><![CDATA[-1]]></add>
<add key="VBdocman_generateJscriptSyntax"><![CDATA[-1]]></add>
<add key="VBdocman_supportedPlatforms"><![CDATA[Windows 98, Windows 2000 SP4, Windows Millennium Edition, Windows Server 2003, Windows XP Media Center Edition, Windows XP Professional x64 Edition, Windows XP SP2, Windows XP Starter Edition]]></add>
<add key="VBdocman_supportedNetFramework"><![CDATA[3.0, 2.0, 1.1, 1.0]]></add>
<add key="VBdocman_supportedNetCompactFramework"><![CDATA[2.0, 1.0]]></add>
<add key="VBdocman_supportedXnaFramework"><![CDATA[1.0]]></add>
<add key="VBdocman_customVar1"><![CDATA[]]></add>
<add key="VBdocman_customVar2"><![CDATA[]]></add>
<add key="VBdocman_customVar3"><![CDATA[]]></add>
<add key="VBdocman_titlePageText"><![CDATA[]]></add>
<add key="VBdocman_rootNamespaceText"><![CDATA[]]></add>
<add key="VBdocman_rootNamespaceCommentStyle"><![CDATA[2]]></add>
<add key="VBdocman_pageFooterText"><![CDATA[Generated by VSdocman]]></add>
<add key="VBdocman_outputPath"><![CDATA[$(ProjectDir)VSdoc]]></add>
<add key="VBdocman_templateFolder"><![CDATA[$(VSdocmanDir)Templates]]></add>
<add key="VBdocman_externalFilesFolder"><![CDATA[]]></add>
<add key="VBdocman_fileNamingConvention"><![CDATA[1]]></add>
<add key="VBdocman_templateLocale"><![CDATA[en-US]]></add>
<add key="VBdocman_templatePath"><![CDATA[chm_msdn2.vbdt]]></add>
<add key="VBdocman_enumSorting"><![CDATA[1]]></add>
<add key="VBdocman_helpTitle"><![CDATA[ImageProcessor Reference]]></add>
<add key="VBdocman_customTopics"><![CDATA[<?xml version="1.0" encoding="utf-8"?>
<topics>
<topic>
<type>normal</type>
<is-default>yes</is-default>
<name>ImageProcessor Reference</name>
<id>imageprocessor_reference</id>
<comment><![CDATA[<summary></summary>vsdocman_escaped_]_]_></comment>
<namespaces />
<topics>
<topic>
<type>placeholder</type>
<is-default>no</is-default>
<name />
<id>e3cba20ec62e422b8bb39e6be7ca2142</id>
<comment><![CDATA[vsdocman_escaped_]_]_></comment>
<namespaces />
<topics />
</topic>
</topics>
</topic>
</topics>]]></add>
<add key="VBdocman_linkForExternalNotInFramework"><![CDATA[0]]></add>
<add key="VBdocman_allowMacrosInComments"><![CDATA[0]]></add>
<add key="VBdocman_emptyOutputFolder"><![CDATA[0]]></add>
<add key="VBdocman_comModules_Helpers...Extensions...EnumExtensions.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Helpers...Extensions...StringExtensions.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_ImageFactory.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Imaging...ImageUtils.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Imaging...OctreeQuantizer.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Imaging...Quantizer.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Imaging...ResponseType.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Processors...Alpha.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Processors...Class1.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Processors...Crop.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Processors...Filter.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Processors...Quality.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Processors...Resize.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Processors...Vignette.cs"><![CDATA[On]]></add>
<add key="VBdocman_comModules_Properties...AssemblyInfo.cs"><![CDATA[On]]></add>
</appSettings>
</configuration>

174
src/ImageProcessor/Imaging/ImageUtils.cs

@ -0,0 +1,174 @@
// -----------------------------------------------------------------------
// <copyright file="ImageUtils.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Imaging
{
#region Using
using System;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
#endregion
/// <summary>
/// Encapsulates useful image utility methods.
/// </summary>
public static class ImageUtils
{
/// <summary>
/// Returns the correct response type based on the given file extension.
/// </summary>
/// <param name="fileName">The string containing the filename to check against.</param>
/// <returns>The correct response type based on the given file extension.</returns>
public static ResponseType GetResponseType(string fileName)
{
string extension = Path.GetExtension(fileName);
if (extension != null)
{
string ext = extension.ToUpperInvariant();
switch (ext)
{
case ".PNG":
return ResponseType.Png;
case ".BMP":
return ResponseType.Bmp;
case ".GIF":
return ResponseType.Gif;
default:
// Should be a jpeg.
return ResponseType.Jpeg;
}
}
// TODO: Should we call this on bad request?
return ResponseType.Jpeg;
}
/// <summary>
/// Returns the correct image format based on the given file extension.
/// </summary>
/// <param name="fileName">The string containing the filename to check against.</param>
/// <returns>The correct image format based on the given filename.</returns>
public static ImageFormat GetImageFormat(string fileName)
{
string extension = Path.GetExtension(fileName);
if (extension != null)
{
string ext = extension.ToUpperInvariant();
switch (ext)
{
case ".PNG":
return ImageFormat.Png;
case ".BMP":
return ImageFormat.Bmp;
case ".GIF":
return ImageFormat.Gif;
default:
// Should be a jpeg.
return ImageFormat.Jpeg;
}
}
// TODO: SHow custom exception??
return null;
}
/// <summary>
/// Returns the correct image format based on the given response type.
/// </summary>
/// <param name="responseType">
/// The <see cref="ImageProcessor.Imaging.ResponseType"/> to check against.
/// </param>
/// <returns>The correct image format based on the given response type.</returns>
public static ImageFormat GetImageFormat(ResponseType responseType)
{
switch (responseType)
{
case ResponseType.Png:
return ImageFormat.Png;
case ResponseType.Bmp:
return ImageFormat.Bmp;
case ResponseType.Gif:
return ImageFormat.Gif;
default:
// Should be a jpeg.
return ImageFormat.Jpeg;
}
}
/// <summary>
/// Returns the first ImageCodeInfo instance with the specified mime type.
/// </summary>
/// <param name="mimeType">
/// A string that contains the codec's Multipurpose Internet Mail Extensions (MIME) type.
/// </param>
/// <returns>
/// The first ImageCodeInfo instance with the specified mime type.
/// </returns>
public static ImageCodecInfo GetImageCodeInfo(string mimeType)
{
ImageCodecInfo[] info = ImageCodecInfo.GetImageEncoders();
return info.FirstOrDefault(ici => ici.MimeType.Equals(mimeType, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// Returns an instance of EncodingParameters for jpeg comression.
/// </summary>
/// <param name="quality">The quality to return the image at.</param>
/// <returns>The encodingParameters for jpeg comression. </returns>
public static EncoderParameters GetEncodingParameters(int quality)
{
EncoderParameters encoderParameters = null;
try
{
// Create a series of encoder parameters.
encoderParameters = new EncoderParameters(1);
// Set the quality.
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
}
catch
{
if (encoderParameters != null)
{
encoderParameters.Dispose();
}
}
return encoderParameters;
}
/// <summary>
/// Checks a given string to check whether the value contains a valid image extension.
/// </summary>
/// <param name="fileName">The string containing the filename to check.</param>
/// <returns>True the value contains a valid image extension, otherwise false.</returns>
public static bool IsValidImageExtension(string fileName)
{
bool isValid = false;
if (!string.IsNullOrWhiteSpace(fileName))
{
string[] fileExtensions = { ".BMP", ".JPG", ".PNG", ".GIF", ".JPEG" };
Parallel.ForEach(
fileExtensions,
(extension, loop) =>
{
if (fileName.ToUpperInvariant().EndsWith(extension))
{
isValid = true;
loop.Stop();
}
});
}
return isValid;
}
}
}

512
src/ImageProcessor/Imaging/OctreeQuantizer.cs

@ -0,0 +1,512 @@
// -----------------------------------------------------------------------
// <copyright file="OctreeQuantizer.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Imaging
{
#region Using
using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
#endregion
/// <summary>
/// Encapsulates methods to calculate the colour palette if an image using an octree pattern.
/// </summary>
internal class OctreeQuantizer : Quantizer
{
#region Fields
/// <summary>
/// Stores the tree.
/// </summary>
private readonly Octree octree;
/// <summary>
/// The maximum allowed color depth.
/// </summary>
private readonly int maxColors;
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="T:ImageProcessor.Imaging.OctreeQuantizer">OctreeQuantizer</see> class.
/// </summary>
/// <remarks>
/// The Octree quantizer is a two pass algorithm. The initial pass sets up the octree,
/// the second pass quantizes a colour based on the nodes in the tree
/// </remarks>
/// <param name="maxColors">The maximum number of colours to return, maximum 255.</param>
/// <param name="maxColorBits">The number of significant bits minimum 1, maximum 8.</param>
public OctreeQuantizer(int maxColors, int maxColorBits)
: base(false)
{
if (maxColors > 255)
{
throw new ArgumentOutOfRangeException("maxColors", maxColors, "The number of colours should be less than 256");
}
if ((maxColorBits < 1) | (maxColorBits > 8))
{
throw new ArgumentOutOfRangeException("maxColorBits", maxColorBits, "This should be between 1 and 8");
}
// Construct the octree
this.octree = new Octree(maxColorBits);
this.maxColors = maxColors;
}
/// <summary>
/// Process the pixel in the first pass of the algorithm.
/// </summary>
/// <param name="pixel">The pixel to quantize</param>
/// <remarks>
/// This function need only be overridden if your quantize algorithm needs two passes,
/// such as an Octree quantizer.
/// </remarks>
protected override void InitialQuantizePixel(Color32 pixel)
{
// Add the colour to the octree
this.octree.AddColor(pixel);
}
/// <summary>
/// Override this to process the pixel in the second pass of the algorithm.
/// </summary>
/// <param name="pixel">The pixel to quantize</param>
/// <returns>The quantized value.</returns>
protected override byte QuantizePixel(Color32 pixel)
{
// The colour at [this.maxColors] is set to transparent
byte paletteIndex;
// Get the palette index if this non-transparent
if (pixel.Alpha > 0)
{
paletteIndex = (byte)this.octree.GetPaletteIndex(pixel);
}
else
{
paletteIndex = (byte)this.maxColors;
}
return paletteIndex;
}
/// <summary>
/// Retrieve the palette for the quantized image
/// </summary>
/// <param name="original">Any old palette, this is overwritten</param>
/// <returns>The new colour palette</returns>
protected override ColorPalette GetPalette(ColorPalette original)
{
// First off convert the octree to this.maxColors colours
ArrayList palette = this.octree.Palletize(this.maxColors - 1);
// Then convert the palette based on those colours
for (int index = 0; index < palette.Count; index++)
{
original.Entries[index] = (Color)palette[index];
}
// Add the transparent colour
original.Entries[this.maxColors] = Color.FromArgb(0, 0, 0, 0);
return original;
}
/// <summary>
/// Describes a tree data structure in which each internal node has exactly eight children.
/// </summary>
private class Octree
{
/// <summary>
/// Initializes a new instance of the <see cref="T:ImageProcessor.Imaging.OctreeQuantizer.Octree">Octree</see> class.
/// </summary>
/// <param name="maxColorBits">The maximum number of significant bits in the image</param>
public Octree(int maxColorBits)
{
this._maxColorBits = maxColorBits;
this._leafCount = 0;
this._reducibleNodes = new OctreeNode[9];
this._root = new OctreeNode(0, this._maxColorBits, this);
this._previousColor = 0;
this._previousNode = null;
}
/// <summary>
/// Add a given colour value to the octree
/// </summary>
/// <param name="pixel">
/// The color value to add.
/// </param>
public void AddColor(Color32 pixel)
{
// Check if this request is for the same colour as the last
if (this._previousColor == pixel.ARGB)
{
// If so, check if I have a previous node setup. This will only occur if the first colour in the image
// happens to be black, with an alpha component of zero.
if (null == this._previousNode)
{
this._previousColor = pixel.ARGB;
this._root.AddColor(pixel, this._maxColorBits, 0, this);
}
else
{
// Just update the previous node
this._previousNode.Increment(pixel);
}
}
else
{
this._previousColor = pixel.ARGB;
this._root.AddColor(pixel, this._maxColorBits, 0, this);
}
}
/// <summary>
/// Reduce the depth of the tree
/// </summary>
public void Reduce()
{
// Find the deepest level containing at least one reducible node
int index = this._maxColorBits - 1;
while ((index > 0) && (this._reducibleNodes[index] == null))
{
index--;
}
// Reduce the node most recently added to the list at level 'index'
OctreeNode node = this._reducibleNodes[index];
this._reducibleNodes[index] = node.NextReducible;
// Decrement the leaf count after reducing the node
this._leafCount -= node.Reduce();
// And just in case I've reduced the last color to be added, and the next color to
// be added is the same, invalidate the previousNode...
this._previousNode = null;
}
/// <summary>
/// Get or sets the number of leaves in the tree
/// </summary>
public int Leaves
{
get { return this._leafCount; }
set { this._leafCount = value; }
}
/// <summary>
/// Return the array of reducible nodes
/// </summary>
protected OctreeNode[] ReducibleNodes
{
get { return this._reducibleNodes; }
}
/// <summary>
/// Keep track of the previous node that was quantized
/// </summary>
/// <param name="node">The node last quantized</param>
protected void TrackPrevious(OctreeNode node)
{
this._previousNode = node;
}
/// <summary>
/// Convert the nodes in the octree to a palette with a maximum of colorCount colours
/// </summary>
/// <param name="colorCount">The maximum number of colours</param>
/// <returns>An array list with the palletized colours</returns>
public ArrayList Palletize(int colorCount)
{
while (this.Leaves > colorCount)
{
this.Reduce();
}
// Now palletize the nodes
ArrayList palette = new ArrayList(this.Leaves);
int paletteIndex = 0;
this._root.ConstructPalette(palette, ref paletteIndex);
// And return the palette
return palette;
}
/// <summary>
/// Get the palette index for the passed colour
/// </summary>
/// <param name="pixel"></param>
/// <returns></returns>
public int GetPaletteIndex(Color32 pixel)
{
return this._root.GetPaletteIndex(pixel, 0);
}
/// <summary>
/// Mask used when getting the appropriate pixels for a given node
/// </summary>
private static int[] mask = new int[8] { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
/// <summary>
/// The root of the octree
/// </summary>
private OctreeNode _root;
/// <summary>
/// Number of leaves in the tree
/// </summary>
private int _leafCount;
/// <summary>
/// Array of reducible nodes
/// </summary>
private OctreeNode[] _reducibleNodes;
/// <summary>
/// Maximum number of significant bits in the image
/// </summary>
private int _maxColorBits;
/// <summary>
/// Store the last node quantized
/// </summary>
private OctreeNode _previousNode;
/// <summary>
/// Cache the previous color quantized
/// </summary>
private int _previousColor;
/// <summary>
/// Class which encapsulates each node in the tree
/// </summary>
protected class OctreeNode
{
/// <summary>
/// Construct the node
/// </summary>
/// <param name="level">The level in the tree = 0 - 7</param>
/// <param name="colorBits">The number of significant color bits in the image</param>
/// <param name="octree">The tree to which this node belongs</param>
public OctreeNode(int level, int colorBits, Octree octree)
{
// Construct the new node
this._leaf = (level == colorBits);
this._red = _green = _blue = 0;
this._pixelCount = 0;
// If a leaf, increment the leaf count
if (this._leaf)
{
octree.Leaves++;
this._nextReducible = null;
this._children = null;
}
else
{
// Otherwise add this to the reducible nodes
this._nextReducible = octree.ReducibleNodes[level];
octree.ReducibleNodes[level] = this;
this._children = new OctreeNode[8];
}
}
/// <summary>
/// Add a color into the tree
/// </summary>
/// <param name="pixel">The color</param>
/// <param name="colorBits">The number of significant color bits</param>
/// <param name="level">The level in the tree</param>
/// <param name="octree">The tree to which this node belongs</param>
public void AddColor(Color32 pixel, int colorBits, int level, Octree octree)
{
// Update the color information if this is a leaf
if (this._leaf)
{
Increment(pixel);
// Setup the previous node
octree.TrackPrevious(this);
}
else
{
// Go to the next level down in the tree
int shift = 7 - level;
int index = ((pixel.Red & mask[level]) >> (shift - 2)) |
((pixel.Green & mask[level]) >> (shift - 1)) |
((pixel.Blue & mask[level]) >> (shift));
OctreeNode child = this._children[index];
if (null == child)
{
// Create a new child node & store in the array
child = new OctreeNode(level + 1, colorBits, octree);
this._children[index] = child;
}
// Add the color to the child node
child.AddColor(pixel, colorBits, level + 1, octree);
}
}
/// <summary>
/// Get or Sets the next reducible node
/// </summary>
public OctreeNode NextReducible
{
get { return _nextReducible; }
set { _nextReducible = value; }
}
/// <summary>
/// Return the child nodes
/// </summary>
public OctreeNode[] Children
{
get { return _children; }
}
/// <summary>
/// Reduce this node by removing all of its children
/// </summary>
/// <returns>The number of leaves removed</returns>
public int Reduce()
{
this._red = this._green = this._blue = 0;
int children = 0;
// Loop through all children and add their information to this node
for (int index = 0; index < 8; index++)
{
if (null != this._children[index])
{
this._red += this._children[index]._red;
this._green += this._children[index]._green;
this._blue += this._children[index]._blue;
this._pixelCount += this._children[index]._pixelCount;
++children;
this._children[index] = null;
}
}
// Now change this to a leaf node
this._leaf = true;
// Return the number of nodes to decrement the leaf count by
return children - 1;
}
/// <summary>
/// Traverse the tree, building up the color palette
/// </summary>
/// <param name="palette">The palette</param>
/// <param name="paletteIndex">The current palette index</param>
public void ConstructPalette(ArrayList palette, ref int paletteIndex)
{
if (_leaf)
{
// Consume the next palette index
_paletteIndex = paletteIndex++;
// And set the color of the palette entry
palette.Add(Color.FromArgb(_red / _pixelCount, _green / _pixelCount, _blue / _pixelCount));
}
else
{
// Loop through children looking for leaves
for (int index = 0; index < 8; index++)
{
if (null != _children[index])
_children[index].ConstructPalette(palette, ref paletteIndex);
}
}
}
/// <summary>
/// Return the palette index for the passed color
/// </summary>
public int GetPaletteIndex(Color32 pixel, int level)
{
int paletteIndex = _paletteIndex;
if (!_leaf)
{
int shift = 7 - level;
int index = ((pixel.Red & mask[level]) >> (shift - 2)) |
((pixel.Green & mask[level]) >> (shift - 1)) |
((pixel.Blue & mask[level]) >> (shift));
if (null != _children[index])
{
paletteIndex = _children[index].GetPaletteIndex(pixel, level + 1);
}
else
{
throw new Exception("Didn't expect this!");
}
}
return paletteIndex;
}
/// <summary>
/// Increment the pixel count and add to the color information
/// </summary>
public void Increment(Color32 pixel)
{
this._pixelCount++;
this._red += pixel.Red;
this._green += pixel.Green;
this._blue += pixel.Blue;
}
/// <summary>
/// Flag indicating that this is a leaf node
/// </summary>
private bool _leaf;
/// <summary>
/// Number of pixels in this node
/// </summary>
private int _pixelCount;
/// <summary>
/// Red component
/// </summary>
private int _red;
/// <summary>
/// Green Component
/// </summary>
private int _green;
/// <summary>
/// Blue component
/// </summary>
private int _blue;
/// <summary>
/// Pointers to any child nodes
/// </summary>
private OctreeNode[] _children;
/// <summary>
/// Pointer to next reducible node
/// </summary>
private OctreeNode _nextReducible;
/// <summary>
/// The index of this node in the palette
/// </summary>
private int _paletteIndex;
}
}
}
}

138
src/ImageProcessor/Imaging/PaletteQuantizer.cs

@ -0,0 +1,138 @@
// -----------------------------------------------------------------------
// <copyright file="PaletteQuantizer.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Imaging
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
/// <summary>
/// Encapsulates methods to calculate the colour palette if an image.
/// http://codebetter.com/brendantompkins/2007/06/14/gif-image-color-quantizer-now-with-safe-goodness/
/// </summary>
internal class PaletteQuantizer : Quantizer
{
/// <summary>
/// Initializes a new instance of the <see cref="PaletteQuantizer"/> class.
/// </summary>
/// <param name="palette">
/// The color palette to quantize to
/// </param>
/// <remarks>
/// Palette quantization only requires a single quantization step
/// </remarks>
public PaletteQuantizer(ArrayList palette)
: base(true)
{
_colorMap = new Hashtable();
_colors = new Color[palette.Count];
palette.CopyTo(_colors);
}
/// <summary>
/// Override this to process the pixel in the second pass of the algorithm
/// </summary>
/// <param name="pixel">The pixel to quantize</param>
/// <returns>The quantized value</returns>
protected override byte QuantizePixel(Color32 pixel)
{
byte colorIndex = 0;
int colorHash = pixel.ARGB;
// Check if the color is in the lookup table
if (_colorMap.ContainsKey(colorHash))
{
colorIndex = (byte)_colorMap[colorHash];
}
else
{
// Not found - loop through the palette and find the nearest match.
// Firstly check the alpha value - if 0, lookup the transparent color
if (0 == pixel.Alpha)
{
// Transparent. Lookup the first color with an alpha value of 0
for (int index = 0; index < _colors.Length; index++)
{
if (0 == _colors[index].A)
{
colorIndex = (byte)index;
break;
}
}
}
else
{
// Not transparent...
int leastDistance = int.MaxValue;
int red = pixel.Red;
int green = pixel.Green;
int blue = pixel.Blue;
// Loop through the entire palette, looking for the closest color match
for (int index = 0; index < _colors.Length; index++)
{
Color paletteColor = _colors[index];
int redDistance = paletteColor.R - red;
int greenDistance = paletteColor.G - green;
int blueDistance = paletteColor.B - blue;
int distance = (redDistance * redDistance) +
(greenDistance * greenDistance) +
(blueDistance * blueDistance);
if (distance < leastDistance)
{
colorIndex = (byte)index;
leastDistance = distance;
// And if it's an exact match, exit the loop
if (0 == distance)
{
break;
}
}
}
}
// Now I have the color, pop it into the hashtable for next time
_colorMap.Add(colorHash, colorIndex);
}
return colorIndex;
}
/// <summary>
/// Retrieve the palette for the quantized image
/// </summary>
/// <param name="palette">Any old palette, this is overrwritten</param>
/// <returns>The new color palette</returns>
protected override ColorPalette GetPalette(ColorPalette palette)
{
for (int index = 0; index < _colors.Length; index++)
{
palette.Entries[index] = _colors[index];
}
return palette;
}
/// <summary>
/// Lookup table for colors
/// </summary>
private Hashtable _colorMap;
/// <summary>
/// List of all colors in the palette
/// </summary>
protected Color[] _colors;
}
}

314
src/ImageProcessor/Imaging/Quantizer.cs

@ -0,0 +1,314 @@
// -----------------------------------------------------------------------
// <copyright file="Quantizer.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Imaging
{
#region Using
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
#endregion
/// <summary>
/// Encapsulates methods to calulate the colour pallete of an image.
/// </summary>
internal abstract class Quantizer
{
#region Fields
/// <summary>
/// The flag used to indicate whether a single pass or two passes are needed for quantization.
/// </summary>
private readonly bool singlePass;
/// <summary>
/// The size in bytes of the 32 bpp Colour structure.
/// </summary>
private readonly int pixelSize;
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="T:ImageProcessor.Imaging.Quantizer">Quantizer</see> class.
/// </summary>
/// <param name="singlePass">
/// If set to <see langword="true"/>, then the quantizer will loop through the source pixels once;
/// otherwise, <see langword="false"/>.
/// </param>
protected Quantizer(bool singlePass)
{
this.singlePass = singlePass;
this.pixelSize = Marshal.SizeOf(typeof(Color32));
}
/// <summary>
/// Quantizes the given <see cref="T:System.Drawing.Image">Image</see> and returns the resulting output
/// <see cref="T:System.Drawing.Bitmap">Bitmap.</see>
/// </summary>
/// <param name="source">The image to quantize</param>
/// <returns>
/// A quantized <see cref="T:System.Drawing.Bitmap">Bitmap</see> version of the <see cref="T:System.Drawing.Image">Image</see>
/// </returns>
public Bitmap Quantize(Image source)
{
// Get the size of the source image
int height = source.Height;
int width = source.Width;
// And construct a rectangle from these dimensions
Rectangle bounds = new Rectangle(0, 0, width, height);
// First off take a 32bpp copy of the image
using (Bitmap copy = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
Bitmap output = null;
// Define a pointer to the bitmap data
BitmapData sourceData = null;
try
{
// And construct an 8bpp version
output = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
// Now lock the bitmap into memory
using (Graphics graphics = Graphics.FromImage(copy))
{
graphics.PageUnit = GraphicsUnit.Pixel;
// Draw the source image onto the copy bitmap,
// which will effect a widening as appropriate.
graphics.DrawImage(source, bounds);
}
// Get the source image bits and lock into memory
sourceData = copy.LockBits(bounds, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
// Call the FirstPass function if not a single pass algorithm.
// For something like an octree quantizer, this will run through
// all image pixels, build a data structure, and create a palette.
if (!this.singlePass)
{
this.FirstPass(sourceData, width, height);
}
// Then set the colour palette on the output bitmap. I'm passing in the current palette
// as there's no way to construct a new, empty palette.
output.Palette = this.GetPalette(output.Palette);
// Then call the second pass which actually does the conversion
this.SecondPass(sourceData, output, width, height, bounds);
}
catch
{
if (output != null)
{
output.Dispose();
}
}
finally
{
// Ensure that the bits are unlocked
copy.UnlockBits(sourceData);
}
// Last but not least, return the output bitmap
return output;
}
}
/// <summary>
/// Execute the first pass through the pixels in the image
/// </summary>
/// <param name="sourceData">The source data</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
protected virtual void FirstPass(BitmapData sourceData, int width, int height)
{
// Define the source data pointers. The source row is a byte to
// keep addition of the stride value easier (as this is in bytes)
IntPtr sourceRow = sourceData.Scan0;
// Loop through each row
for (int row = 0; row < height; row++)
{
// Set the source pixel to the first pixel in this row
IntPtr sourcePixel = sourceRow;
// And loop through each column
for (int col = 0; col < width; col++)
{
this.InitialQuantizePixel(new Color32(sourcePixel));
sourcePixel = (IntPtr)((int)sourcePixel + this.pixelSize);
}
// Now I have the pixel, call the FirstPassQuantize function.
// Add the stride to the source row
sourceRow = (IntPtr)((long)sourceRow + sourceData.Stride);
}
}
/// <summary>
/// Execute a second pass through the bitmap
/// </summary>
/// <param name="sourceData">The source bitmap, locked into memory</param>
/// <param name="output">The output bitmap</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
/// <param name="bounds">The bounding rectangle</param>
protected virtual void SecondPass(BitmapData sourceData, Bitmap output, int width, int height, Rectangle bounds)
{
BitmapData outputData = null;
try
{
// Lock the output bitmap into memory
outputData = output.LockBits(bounds, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
// Define the source data pointers. The source row is a byte to
// keep addition of the stride value easier (as this is in bytes)
IntPtr sourceRow = sourceData.Scan0;
IntPtr sourcePixel = sourceRow;
IntPtr previousPixel = sourcePixel;
// Now define the destination data pointers
IntPtr destinationRow = outputData.Scan0;
IntPtr destinationPixel = destinationRow;
// And convert the first pixel, so that I have values going into the loop.
byte pixelValue = this.QuantizePixel(new Color32(sourcePixel));
// Assign the value of the first pixel
Marshal.WriteByte(destinationPixel, pixelValue);
// Loop through each row
for (int row = 0; row < height; row++)
{
// Set the source pixel to the first pixel in this row
sourcePixel = sourceRow;
// And set the destination pixel pointer to the first pixel in the row
destinationPixel = destinationRow;
// Loop through each pixel on this scan line
for (int col = 0; col < width; col++)
{
// Check if this is the same as the last pixel. If so use that value
// rather than calculating it again. This is an inexpensive optimisation.
if (Marshal.ReadByte(previousPixel) != Marshal.ReadByte(sourcePixel))
{
// Quantize the pixel
pixelValue = this.QuantizePixel(new Color32(sourcePixel));
// And setup the previous pointer
previousPixel = sourcePixel;
}
// And set the pixel in the output
Marshal.WriteByte(destinationPixel, pixelValue);
sourcePixel = (IntPtr)((long)sourcePixel + this.pixelSize);
destinationPixel = (IntPtr)((long)destinationPixel + 1);
}
// Add the stride to the source row
sourceRow = (IntPtr)((long)sourceRow + sourceData.Stride);
// And to the destination row
destinationRow = (IntPtr)((long)destinationRow + outputData.Stride);
}
}
finally
{
// Ensure that I unlock the output bits
output.UnlockBits(outputData);
}
}
/// <summary>
/// Override this to process the pixel in the first pass of the algorithm
/// </summary>
/// <param name="pixel">The pixel to quantize</param>
/// <remarks>
/// This function need only be overridden if your quantize algorithm needs two passes,
/// such as an Octree quantizer.
/// </remarks>
protected virtual void InitialQuantizePixel(Color32 pixel)
{
}
/// <summary>
/// Override this to process the pixel in the second pass of the algorithm.
/// </summary>
/// <param name="pixel">The pixel to quantize</param>
/// <returns>The quantized value.</returns>
protected abstract byte QuantizePixel(Color32 pixel);
/// <summary>
/// Retrieve the palette for the quantized image
/// </summary>
/// <param name="original">Any old palette, this is overwritten</param>
/// <returns>The new colour palette</returns>
protected abstract ColorPalette GetPalette(ColorPalette original);
/// <summary>
/// Structure that defines a 32 bpp colour
/// </summary>
/// <remarks>
/// This structure is used to read data from a 32 bits per pixel image
/// in memory, and is ordered in this manner as this is the way that
/// the data is laid out in memory
/// </remarks>
[StructLayout(LayoutKind.Explicit)]
public struct Color32
{
/// <summary>
/// Holds the blue component of the colour
/// </summary>
[FieldOffset(0)]
public byte Blue;
/// <summary>
/// Holds the green component of the colour
/// </summary>
[FieldOffset(1)]
public byte Green;
/// <summary>
/// Holds the red component of the colour
/// </summary>
[FieldOffset(2)]
public byte Red;
/// <summary>
/// Holds the alpha component of the colour
/// </summary>
[FieldOffset(3)]
public byte Alpha;
/// <summary>
/// Permits the color32 to be treated as an int32
/// </summary>
[FieldOffset(0)]
public int ARGB;
/// <summary>
/// Initializes a new instance of the <see cref="T:ImageProcessor.Imaging.Quantizer.Color32">Color32</see> structure.
/// </summary>
/// <param name="sourcePixel">The pointer to the pixel.</param>
public Color32(IntPtr sourcePixel)
{
this = (Color32)Marshal.PtrToStructure(sourcePixel, typeof(Color32));
}
/// <summary>
/// Gets the colour for this Color32 object
/// </summary>
public Color Color
{
get { return Color.FromArgb(this.Alpha, this.Red, this.Green, this.Blue); }
}
}
}
}

62
src/ImageProcessor/Imaging/ResponseType.cs

@ -0,0 +1,62 @@
// -----------------------------------------------------------------------
// <copyright file="ResponseType.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Imaging
{
#region Using
using System.ComponentModel;
#endregion
/// <summary>
/// Globally available enumeration which specifies the correct HTTP MIME type of
/// the output stream for different response types.
/// <para>
/// http://en.wikipedia.org/wiki/Internet_media_type"/
/// </para>
/// </summary>
public enum ResponseType
{
#region Image
/// <summary>
/// The correct HTTP MIME type of the output stream for bmp images.
/// </summary>
[DescriptionAttribute("image/bmp")]
Bmp,
/// <summary>
/// The correct HTTP MIME type of the output stream for gif images.
/// </summary>
[DescriptionAttribute("image/gif")]
Gif,
/// <summary>
/// The correct HTTP MIME type of the output stream for jpeg images.
/// </summary>
[DescriptionAttribute("image/jpeg")]
Jpeg,
/// <summary>
/// The correct HTTP MIME type of the output stream for png images.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Png", Justification = "File extension name")]
[DescriptionAttribute("image/png")]
Png,
/// <summary>
/// The correct HTTP MIME type of the output stream for svg images.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Svg", Justification = "File extension name")]
[DescriptionAttribute("image/svg+xml")]
Svg,
/// <summary>
/// The correct HTTP MIME type of the output stream for tiff images.
/// </summary>
[DescriptionAttribute("image/tiff")]
Tiff,
#endregion
}
}

175
src/ImageProcessor/Processors/Alpha.cs

@ -0,0 +1,175 @@
// -----------------------------------------------------------------------
// <copyright file="Alpha.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Processors
{
#region Using
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using ImageProcessor.Helpers.Extensions;
#endregion
/// <summary>
/// Encapsulates methods to change the alpha component of the image to effect its transparency.
/// </summary>
public class Alpha : IGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// http://stackoverflow.com/a/6400969/427899
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"alpha=\d{1,2}(?!\d)|alpha=100", RegexOptions.Compiled);
#region IGraphicsProcessor Members
/// <summary>
/// Gets the name.
/// </summary>
public string Name
{
get
{
return "Alpha";
}
}
/// <summary>
/// Gets the description.
/// </summary>
public string Description
{
get
{
return "Changes the alpha component of the image to effect its transparency.";
}
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets or sets DynamicParameter.
/// </summary>
public dynamic DynamicParameter
{
get;
set;
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get;
private set;
}
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
public Dictionary<string, string> Settings
{
get;
set;
}
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
int percentage = match.Value.ToIntegerArray()[0];
this.DynamicParameter = percentage;
}
index += 1;
}
}
return this.SortOrder;
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public Image ProcessImage(ImageFactory factory)
{
Bitmap newImage = null;
Image image = factory.Image;
try
{
int alphaPercent = this.DynamicParameter;
newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb) { Tag = image.Tag };
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.Matrix00 = colorMatrix.Matrix11 = colorMatrix.Matrix22 = colorMatrix.Matrix44 = 1;
colorMatrix.Matrix33 = (float)alphaPercent / 100;
using (Graphics graphics = Graphics.FromImage(newImage))
{
using (ImageAttributes imageAttributes = new ImageAttributes())
{
imageAttributes.SetColorMatrix(colorMatrix);
graphics.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttributes);
image.Dispose();
image = newImage;
}
}
}
catch
{
if (newImage != null)
{
newImage.Dispose();
}
}
return image;
}
#endregion
}
}

210
src/ImageProcessor/Processors/Crop.cs

@ -0,0 +1,210 @@
// -----------------------------------------------------------------------
// <copyright file="Crop.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Processors
{
#region Using
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using ImageProcessor.Helpers.Extensions;
#endregion
/// <summary>
/// Crops an image to the given directions.
/// </summary>
public class Crop : IGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// http://stackoverflow.com/a/6400969/427899
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"crop=\d+-\d+-\d+-\d+", RegexOptions.Compiled);
#region IGraphicsProcessor Members
/// <summary>
/// Gets the name.
/// </summary>
public string Name
{
get
{
return "Crop";
}
}
/// <summary>
/// Gets the description.
/// </summary>
public string Description
{
get
{
return "Crops an image to the given directions.";
}
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets or sets DynamicParameter.
/// </summary>
public dynamic DynamicParameter
{
get;
set;
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get;
private set;
}
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
public Dictionary<string, string> Settings
{
get;
set;
}
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
int[] coordinates = match.Value.ToIntegerArray();
int x = coordinates[0];
int y = coordinates[1];
int width = coordinates[2];
int height = coordinates[3];
Rectangle rectangle = new Rectangle(x, y, width, height);
this.DynamicParameter = rectangle;
}
index += 1;
}
}
return this.SortOrder;
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public Image ProcessImage(ImageFactory factory)
{
Bitmap newImage = null;
Image image = factory.Image;
try
{
Rectangle rectangle = this.DynamicParameter;
int sourceWidth = image.Width;
int sourceHeight = image.Height;
if (rectangle.X < sourceWidth && rectangle.Y < sourceHeight)
{
if (rectangle.Width > (sourceWidth - rectangle.X))
{
rectangle.Width = sourceWidth - rectangle.X;
}
if (rectangle.Height > (sourceHeight - rectangle.Y))
{
rectangle.Height = sourceHeight - rectangle.Y;
}
newImage = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb) { Tag = image.Tag };
using (Graphics graphics = Graphics.FromImage(newImage))
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
// An unwanted border appears when using InterpolationMode.HighQualityBicubic to resize the image
// as the algorithm appears to be pulling averaging detail from surrounding pixels beyond the edge
// of the image. Using the ImageAttributes class to specify that the pixels beyond are simply mirror
// images of the pixels within solves this problem.
using (ImageAttributes wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(
image,
new Rectangle(0, 0, rectangle.Width, rectangle.Height),
rectangle.X,
rectangle.Y,
rectangle.Width,
rectangle.Height,
GraphicsUnit.Pixel,
wrapMode);
}
// Reassign the image.
image.Dispose();
image = newImage;
}
}
}
catch
{
if (newImage != null)
{
newImage.Dispose();
}
}
return image;
}
#endregion
}
}

292
src/ImageProcessor/Processors/Filter.cs

@ -0,0 +1,292 @@
// -----------------------------------------------------------------------
// <copyright file="Filter.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Processors
{
#region Using
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
#endregion
/// <summary>
/// Encapsulates methods with which to add filters to an image.
/// </summary>
public class Filter : IGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"filter=(lomograph|polaroid|blackwhite|sepia|greyscale)", RegexOptions.Compiled);
#region IGraphicsProcessor Members
/// <summary>
/// Gets the name.
/// </summary>
public string Name
{
get
{
return "Filter";
}
}
/// <summary>
/// Gets the description.
/// </summary>
public string Description
{
get
{
return "Encapsulates methods with which to add filters to an image. e.g polaroid, lomograph";
}
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets or sets DynamicParameter.
/// </summary>
public dynamic DynamicParameter
{
get;
set;
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get;
private set;
}
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
public Dictionary<string, string> Settings
{
get;
set;
}
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
this.DynamicParameter = match.Value.Split('=')[1];
}
index += 1;
}
}
return this.SortOrder;
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public Image ProcessImage(ImageFactory factory)
{
Bitmap newImage = null;
Image image = factory.Image;
try
{
newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb) { Tag = image.Tag };
ColorMatrix colorMatrix = null;
switch ((string)this.DynamicParameter)
{
case "polaroid":
colorMatrix = ColorMatrixes.Poloroid;
break;
case "lomograph":
colorMatrix = ColorMatrixes.Lomograph;
break;
case "sepia":
colorMatrix = ColorMatrixes.Sepia;
break;
case "blackwhite":
colorMatrix = ColorMatrixes.BlackWhite;
break;
case "greyscale":
colorMatrix = ColorMatrixes.GreyScale;
break;
}
using (Graphics graphics = Graphics.FromImage(newImage))
{
using (ImageAttributes attributes = new ImageAttributes())
{
if (colorMatrix != null)
{
attributes.SetColorMatrix(colorMatrix);
}
Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height);
graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes);
}
}
// Reassign the image.
image.Dispose();
image = newImage;
}
catch
{
if (newImage != null)
{
newImage.Dispose();
}
}
return image;
}
#endregion
/// <summary>
/// A list of available color matrices to apply to an image.
/// </summary>
private static class ColorMatrixes
{
/// <summary>
/// Gets Sepia.
/// </summary>
internal static ColorMatrix Sepia
{
get
{
return new ColorMatrix(
new float[][]
{
new float[] { .393f, .349f, .272f, 0, 0 },
new float[] { .769f, .686f, .534f, 0, 0 },
new float[] { .189f, .168f, .131f, 0, 0 },
new float[] { 0, 0, 0, 1, 0 },
new float[] { 0, 0, 0, 0, 1 }
});
}
}
/// <summary>
/// Gets BlackWhite.
/// </summary>
internal static ColorMatrix BlackWhite
{
get
{
return new ColorMatrix(
new float[][]
{
new float[] { 1.5f, 1.5f, 1.5f, 0, 0 },
new float[] { 1.5f, 1.5f, 1.5f, 0, 0 },
new float[] { 1.5f, 1.5f, 1.5f, 0, 0 },
new float[] { 0, 0, 0, 1, 0 },
new float[] { -1, -1, -1, 0, 1 }
});
}
}
/// <summary>
/// Gets Poloroid.
/// </summary>
internal static ColorMatrix Poloroid
{
get
{
return new ColorMatrix(
new float[][]
{
new float[] { 1.438f, -0.062f, -0.062f, 0, 0 },
new float[] { -0.122f, 1.378f, -0.122f, 0, 0 },
new float[] { -0.016f, -0.016f, 1.483f, 0, 0 },
new float[] { 0, 0, 0, 1, 0 },
new float[] { -0.03f, 0.05f, -0.02f, 0, 1 }
});
}
}
/// <summary>
/// Gets Lomograph.
/// </summary>
internal static ColorMatrix Lomograph
{
get
{
return new ColorMatrix(
new float[][]
{
new float[] { 1.25f, 0, 0, 0, 0 },
new float[] { 0, 1.25f, 0, 0, 0 },
new float[] { 0, 0, 0.94f, 0, 0 },
new float[] { 0, 0, 0, 1, 0 },
new float[] { 0, 0, 0, 0, 1 }
});
}
}
/// <summary>
/// Gets GreyScale.
/// </summary>
internal static ColorMatrix GreyScale
{
get
{
return new ColorMatrix(
new float[][]
{
new float[] { .33f, .33f, .33f, 0, 0 },
new float[] { .59f, .59f, .59f, 0, 0 },
new float[] { .11f, .11f, .11f, 0, 0 },
new float[] { 0, 0, 0, 1, 0 },
new float[] { 0, 0, 0, 0, 1 }
});
}
}
}
}
}

159
src/ImageProcessor/Processors/Format.cs

@ -0,0 +1,159 @@
// -----------------------------------------------------------------------
// <copyright file="Format.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Processors
{
#region Using
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
#endregion
/// <summary>
/// Sets the output of the image to a specific format.
/// </summary>
public class Format : IGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"format=(jpeg|png|bmp|gif)", RegexOptions.Compiled);
#region IGraphicsProcessor Members
/// <summary>
/// Gets the name.
/// </summary>
public string Name
{
get
{
return "Format";
}
}
/// <summary>
/// Gets the description.
/// </summary>
public string Description
{
get
{
return "Sets the output of the image to a specific format.";
}
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets or sets DynamicParameter.
/// </summary>
public dynamic DynamicParameter
{
get;
set;
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get;
private set;
}
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
public Dictionary<string, string> Settings
{
get;
set;
}
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
this.DynamicParameter = match.Value.Split('=')[1];
}
index += 1;
}
}
return this.SortOrder;
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public Image ProcessImage(ImageFactory factory)
{
string format = this.DynamicParameter;
ImageFormat imageFormat;
switch (format)
{
case "png":
imageFormat = ImageFormat.Png;
break;
case "bmp":
imageFormat = ImageFormat.Bmp;
break;
case "gif":
imageFormat = ImageFormat.Gif;
break;
default:
// Should be a jpeg.
imageFormat = ImageFormat.Jpeg;
break;
}
// Set the internal property.
factory.ImageFormat = imageFormat;
return factory.Image;
}
#endregion
}
}

80
src/ImageProcessor/Processors/IGraphicsProcessor.cs

@ -0,0 +1,80 @@
// -----------------------------------------------------------------------
// <copyright file="IGraphicsProcessor.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Processors
{
#region Using
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Drawing;
using System.Text.RegularExpressions;
#endregion
/// <summary>
/// Defines properties and methods for ImageProcessor Plugins.
/// </summary>
public interface IGraphicsProcessor
{
#region MetaData
/// <summary>
/// Gets the name.
/// </summary>
string Name { get; }
/// <summary>
/// Gets the description.
/// </summary>
string Description { get; }
#endregion
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
Regex RegexPattern { get; }
/// <summary>
/// Gets DynamicParameter.
/// </summary>
dynamic DynamicParameter { get; }
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
int SortOrder { get; }
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
Dictionary<string, string> Settings { get; set; }
#region Methods
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
int MatchRegexIndex(string queryString);
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
Image ProcessImage(ImageFactory factory);
#endregion
}
}

147
src/ImageProcessor/Processors/Png8.cs

@ -0,0 +1,147 @@
// -----------------------------------------------------------------------
// <copyright file="Class1.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Processors
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ImageProcessor.Imaging;
/// <summary>
/// TODO: Update summary.
/// </summary>
public class Png8 : IGraphicsProcessor
{
#region Implementation of IGraphicsProcessor
/// <summary>
/// Gets the name.
/// </summary>
public string Name
{
get
{
throw new NotImplementedException();
}
}
/// <summary>
/// Gets the description.
/// </summary>
public string Description
{
get
{
throw new NotImplementedException();
}
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
throw new NotImplementedException();
}
}
/// <summary>
/// Gets DynamicParameter.
/// </summary>
public dynamic DynamicParameter
{
get
{
throw new NotImplementedException();
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get
{
throw new NotImplementedException();
}
}
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
public Dictionary<string, string> Settings
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
throw new NotImplementedException();
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public Image ProcessImage(ImageFactory factory)
{
Bitmap newImage = null;
Image image = factory.Image;
try
{
newImage = new Bitmap(image) { Tag = image.Tag };
using (Graphics graphics = Graphics.FromImage(newImage))
{
ArrayList pallete = new ArrayList();
PaletteQuantizer paletteQuantizer = new PaletteQuantizer(new ArrayList(newImage.Palette.Entries));
newImage = paletteQuantizer.Quantize(newImage);
}
}
catch
{
if (newImage != null)
{
newImage.Dispose();
}
}
return image;
}
#endregion
}
}

142
src/ImageProcessor/Processors/Quality.cs

@ -0,0 +1,142 @@
// -----------------------------------------------------------------------
// <copyright file="Quality.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Processors
{
#region Using
using System.Collections.Generic;
using System.Drawing;
using System.Text.RegularExpressions;
using ImageProcessor.Helpers.Extensions;
#endregion
/// <summary>
/// TODO: Update summary.
/// </summary>
public class Quality : IGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"quality=\d{1,2}(?!\d)|quality=100", RegexOptions.Compiled);
#region IGraphicsProcessor Members
/// <summary>
/// Gets the name.
/// </summary>
public string Name
{
get
{
return "Quality";
}
}
/// <summary>
/// Gets the description.
/// </summary>
public string Description
{
get
{
return "Sets the the quality output for jpeg images.";
}
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets or sets DynamicParameter.
/// </summary>
public dynamic DynamicParameter
{
get;
set;
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get;
private set;
}
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
public Dictionary<string, string> Settings
{
get;
set;
}
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
int percentage = match.Value.ToIntegerArray()[0];
this.DynamicParameter = percentage;
}
index += 1;
}
}
return this.SortOrder;
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public Image ProcessImage(ImageFactory factory)
{
// Set the internal property.
factory.JpegQuality = this.DynamicParameter;
return factory.Image;
}
#endregion
}
}

235
src/ImageProcessor/Processors/Resize.cs

@ -0,0 +1,235 @@
// -----------------------------------------------------------------------
// <copyright file="Resize.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Processors
{
#region Using
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using ImageProcessor.Helpers.Extensions;
#endregion
/// <summary>
/// Resizes an image to the given dimensions.
/// </summary>
public class Resize : IGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"(width|height)=\d+", RegexOptions.Compiled);
#region IGraphicsProcessor Members
/// <summary>
/// Gets the name.
/// </summary>
public string Name
{
get
{
return "Resize";
}
}
/// <summary>
/// Gets the description.
/// </summary>
public string Description
{
get
{
return "Resizes an image to the given dimensions.";
}
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets or sets DynamicParameter.
/// </summary>
public dynamic DynamicParameter
{
get;
set;
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get;
private set;
}
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
public Dictionary<string, string> Settings
{
get;
set;
}
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
Size size = new Size();
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
}
if (match.Value.Contains("width"))
{
size.Width = match.Value.ToIntegerArray()[0];
}
else
{
size.Height = match.Value.ToIntegerArray()[0];
}
index += 1;
}
}
this.DynamicParameter = size;
return this.SortOrder;
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public Image ProcessImage(ImageFactory factory)
{
Bitmap newImage = null;
Image image = factory.Image;
try
{
int width = this.DynamicParameter.Width ?? 0;
int height = this.DynamicParameter.Height ?? 0;
int sourceWidth = image.Width;
int sourceHeight = image.Height;
int defaultMaxWidth;
int defaultMaxHeight;
int.TryParse(this.Settings["MaxWidth"], out defaultMaxWidth);
int.TryParse(this.Settings["MaxHeight"], out defaultMaxHeight);
int maxWidth = defaultMaxWidth > 0 ? defaultMaxWidth : int.MaxValue;
int maxHeight = defaultMaxHeight > 0 ? defaultMaxHeight : int.MaxValue;
// If height or width is not passed we assume that the standard ratio is to be kept.
if (height == 0)
{
// Bit of simple fractional maths here.
float percentWidth = Math.Abs(width / (float)sourceWidth);
height = (int)Math.Floor(sourceHeight * percentWidth);
}
if (width == 0)
{
float percentHeight = Math.Abs(height / (float)sourceHeight);
width = (int)Math.Floor(sourceWidth * percentHeight);
}
if (width > 0 && height > 0 && width <= maxWidth && height <= maxHeight)
{
newImage = new Bitmap(width, height, PixelFormat.Format32bppPArgb) { Tag = image.Tag };
using (Graphics graphics = Graphics.FromImage(newImage))
{
// We want to use two different blending algorithms for enlargement/shrinking.
// Bicubic is better enlarging for whilst Bilinear is better for shrinking.
// http://www.codinghorror.com/blog/2007/07/better-image-resizing.html
if (image.Width < width && image.Height < height)
{
// We are making it larger.
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
}
else
{
// We are making it smaller.
graphics.SmoothingMode = SmoothingMode.None;
// Contrary to everything I have read bicubic is producing the best results.
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.None;
graphics.CompositingQuality = CompositingQuality.HighSpeed;
}
// An unwanted border appears when using InterpolationMode.HighQualityBicubic to resize the image
// as the algorithm appears to be pulling averaging detail from surFlooring pixels beyond the edge
// of the image. Using the ImageAttributes class to specify that the pixels beyond are simply mirror
// images of the pixels within solves this problem.
using (ImageAttributes wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
Rectangle destRect = new Rectangle(0, 0, width, height);
graphics.DrawImage(image, destRect, 0, 0, sourceWidth, sourceHeight, GraphicsUnit.Pixel, wrapMode);
}
// Reassign the image.
image.Dispose();
image = newImage;
}
}
}
catch
{
if (newImage != null)
{
newImage.Dispose();
}
}
return image;
}
#endregion
}
}

200
src/ImageProcessor/Processors/Vignette.cs

@ -0,0 +1,200 @@
// -----------------------------------------------------------------------
// <copyright file="Vignette.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Processors
{
#region Using
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text.RegularExpressions;
#endregion
/// <summary>
/// Encapsulates methods with which to add a vignette image effect to an image.
/// </summary>
public class Vignette : IGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"vignette=true", RegexOptions.Compiled);
#region IGraphicsProcessor Members
/// <summary>
/// Gets the name.
/// </summary>
public string Name
{
get
{
return "Vignette";
}
}
/// <summary>
/// Gets the description.
/// </summary>
public string Description
{
get
{
return "Adds a vignette image effect to the image.";
}
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets or sets DynamicParameter.
/// </summary>
public dynamic DynamicParameter
{
get;
set;
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get;
private set;
}
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
public Dictionary<string, string> Settings
{
get;
set;
}
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
bool doVignette;
this.DynamicParameter = bool.TryParse(match.Value.Split('=')[1], out doVignette);
}
index += 1;
}
}
return this.SortOrder;
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public Image ProcessImage(ImageFactory factory)
{
Bitmap newImage = null;
Image image = factory.Image;
try
{
newImage = new Bitmap(image) { Tag = image.Tag };
using (Graphics graphics = Graphics.FromImage(newImage))
{
Rectangle bounds = new Rectangle(0, 0, newImage.Width, newImage.Height);
Rectangle ellipsebounds = bounds;
// Increase the rectangle size by the difference between the rectangle dimensions and sqrt(2)/2 * the rectangle dimensions.
// Why sqrt(2)/2? Because the point (sqrt(2)/2, sqrt(2)/2) is the 45 degree angle point on a unit circle. Scaling by the width
// and height gives the distance needed to inflate the rect to make sure it's fully covered.
ellipsebounds.Offset(-ellipsebounds.X, -ellipsebounds.Y);
int x = ellipsebounds.Width - (int)Math.Floor(.70712 * ellipsebounds.Width);
int y = ellipsebounds.Height - (int)Math.Floor(.70712 * ellipsebounds.Height);
ellipsebounds.Inflate(x, y);
using (GraphicsPath path = new GraphicsPath())
{
path.AddEllipse(ellipsebounds);
using (PathGradientBrush brush = new PathGradientBrush(path))
{
// Fill a rectangle with an elliptical gradient brush that goes from white to black.
// Change the colour from white to pure transparent black and from black to pure opaque black.
// This has the effect of painting the far corners black and shade less on the way in to the centre.
brush.WrapMode = WrapMode.Tile;
brush.CenterColor = Color.FromArgb(0, 0, 0, 0);
brush.SurroundColors = new Color[] { Color.FromArgb(255, 0, 0, 0) };
Blend blend = new Blend
{
Positions = new float[] { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0F },
Factors = new float[] { 0.0f, 0.5f, 1f, 1f, 1.0f, 1.0f }
};
brush.Blend = blend;
Region oldClip = graphics.Clip;
graphics.Clip = new Region(bounds);
graphics.FillRectangle(brush, ellipsebounds);
graphics.Clip = oldClip;
}
// Reassign the image.
image.Dispose();
image = newImage;
}
}
}
catch
{
if (newImage != null)
{
newImage.Dispose();
}
}
return image;
}
#endregion
}
}

36
src/ImageProcessor/Properties/AssemblyInfo.cs

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ImageProcessor")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ImageProcessor")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("bdaae9bd-0dc8-4b06-8722-e2e0c9a74301")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

20
src/Test/Test.sln

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{30327C08-7574-4D7E-AC95-6A58753C6855}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{30327C08-7574-4D7E-AC95-6A58753C6855}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{30327C08-7574-4D7E-AC95-6A58753C6855}.Debug|Any CPU.Build.0 = Debug|Any CPU
{30327C08-7574-4D7E-AC95-6A58753C6855}.Release|Any CPU.ActiveCfg = Release|Any CPU
{30327C08-7574-4D7E-AC95-6A58753C6855}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

324
src/Test/Test/Content/Site.css

@ -0,0 +1,324 @@
/*----------------------------------------------------------
The base color for this template is #5c87b2. If you'd like
to use a different color start by replacing all instances of
#5c87b2 with your new color.
----------------------------------------------------------*/
body {
background-color: #5c87b2;
font-size: .85em;
font-family: "Trebuchet MS", Verdana, Helvetica, Sans-Serif;
margin: 0;
padding: 0;
color: #696969;
}
a:link {
color: #034af3;
text-decoration: underline;
}
a:visited {
color: #505abc;
}
a:hover {
color: #1d60ff;
text-decoration: none;
}
a:active {
color: #12eb87;
}
p, ul {
margin-bottom: 20px;
line-height: 1.6em;
}
header,
footer,
nav,
section {
display: block;
}
/* HEADINGS
----------------------------------------------------------*/
h1, h2, h3, h4, h5, h6 {
font-size: 1.5em;
color: #000;
}
h1 {
font-size: 2em;
padding-bottom: 0;
margin-bottom: 0;
}
h2 {
padding: 0 0 10px 0;
}
h3 {
font-size: 1.2em;
}
h4 {
font-size: 1.1em;
}
h5, h6 {
font-size: 1em;
}
/* PRIMARY LAYOUT ELEMENTS
----------------------------------------------------------*/
/* you can specify a greater or lesser percentage for the
page width. Or, you can specify an exact pixel width. */
.page {
width: 90%;
margin-left: auto;
margin-right: auto;
}
header, #header {
position: relative;
margin-bottom: 0px;
color: #000;
padding: 0;
}
header h1, #header h1 {
font-weight: bold;
padding: 5px 0;
margin: 0;
color: #fff;
border: none;
line-height: 2em;
font-size: 32px !important;
text-shadow: 1px 1px 2px #111;
}
#main {
padding: 30px 30px 15px 30px;
background-color: #fff;
border-radius: 4px 0 0 0;
-webkit-border-radius: 4px 0 0 0;
-moz-border-radius: 4px 0 0 0;
}
footer,
#footer {
background-color: #fff;
color: #999;
padding: 10px 0;
text-align: center;
line-height: normal;
margin: 0 0 30px 0;
font-size: .9em;
border-radius: 0 0 4px 4px;
-webkit-border-radius: 0 0 4px 4px;
-moz-border-radius: 0 0 4px 4px;
}
/* TAB MENU
----------------------------------------------------------*/
ul#menu {
border-bottom: 1px #5C87B2 solid;
padding: 0 0 2px;
position: relative;
margin: 0;
text-align: right;
}
ul#menu li {
display: inline;
list-style: none;
}
ul#menu li#greeting {
padding: 10px 20px;
font-weight: bold;
text-decoration: none;
line-height: 2.8em;
color: #fff;
}
ul#menu li a {
padding: 10px 20px;
font-weight: bold;
text-decoration: none;
line-height: 2.8em;
background-color: #e8eef4;
color: #034af3;
border-radius: 4px 4px 0 0;
-webkit-border-radius: 4px 4px 0 0;
-moz-border-radius: 4px 4px 0 0;
}
ul#menu li a:hover {
background-color: #fff;
text-decoration: none;
}
ul#menu li a:active {
background-color: #a6e2a6;
text-decoration: none;
}
ul#menu li.selected a {
background-color: #fff;
color: #000;
}
/* FORM LAYOUT ELEMENTS
----------------------------------------------------------*/
fieldset {
border: 1px solid #ddd;
padding: 0 1.4em 1.4em 1.4em;
margin: 0 0 1.5em 0;
}
legend {
font-size: 1.2em;
font-weight: bold;
}
textarea {
min-height: 75px;
}
input[type="text"],
input[type="password"] {
border: 1px solid #ccc;
padding: 2px;
font-size: 1.2em;
color: #444;
width: 200px;
}
select {
border: 1px solid #ccc;
padding: 2px;
font-size: 1.2em;
color: #444;
}
input[type="submit"] {
font-size: 1.2em;
padding: 5px;
}
/* TABLE
----------------------------------------------------------*/
table {
border: solid 1px #e8eef4;
border-collapse: collapse;
}
table td {
padding: 5px;
border: solid 1px #e8eef4;
}
table th {
padding: 6px 5px;
text-align: left;
background-color: #e8eef4;
border: solid 1px #e8eef4;
}
/* MISC
----------------------------------------------------------*/
.clear {
clear: both;
}
.error {
color: Red;
}
nav,
#menucontainer {
margin-top: 40px;
}
div#title {
display: block;
float: left;
text-align: left;
}
#logindisplay {
font-size: 1.1em;
display: block;
text-align: right;
margin: 10px;
color: White;
}
#logindisplay a:link {
color: white;
text-decoration: underline;
}
#logindisplay a:visited {
color: white;
text-decoration: underline;
}
#logindisplay a:hover {
color: white;
text-decoration: none;
}
/* Styles for validation helpers
-----------------------------------------------------------*/
.field-validation-error {
color: #ff0000;
}
.field-validation-valid {
display: none;
}
.input-validation-error {
border: 1px solid #ff0000;
background-color: #ffeeee;
}
.validation-summary-errors {
font-weight: bold;
color: #ff0000;
}
.validation-summary-valid {
display: none;
}
/* Styles for editor and display helpers
----------------------------------------------------------*/
.display-label,
.editor-label {
margin: 1em 0 0 0;
}
.display-field,
.editor-field {
margin: 0.5em 0 0 0;
}
.text-box {
width: 30em;
}
.text-box.multi-line {
height: 6.5em;
}
.tri-state {
width: 6em;
}

BIN
src/Test/Test/Content/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

BIN
src/Test/Test/Content/themes/base/images/ui-bg_flat_75_ffffff_40x100.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

BIN
src/Test/Test/Content/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

BIN
src/Test/Test/Content/themes/base/images/ui-bg_glass_65_ffffff_1x400.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

BIN
src/Test/Test/Content/themes/base/images/ui-bg_glass_75_dadada_1x400.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B

BIN
src/Test/Test/Content/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

BIN
src/Test/Test/Content/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

BIN
src/Test/Test/Content/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

BIN
src/Test/Test/Content/themes/base/images/ui-icons_222222_256x240.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
src/Test/Test/Content/themes/base/images/ui-icons_2e83ff_256x240.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
src/Test/Test/Content/themes/base/images/ui-icons_454545_256x240.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
src/Test/Test/Content/themes/base/images/ui-icons_888888_256x240.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
src/Test/Test/Content/themes/base/images/ui-icons_cd0a0a_256x240.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

24
src/Test/Test/Content/themes/base/jquery.ui.accordion.css

@ -0,0 +1,24 @@
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI Accordion 1.8.11
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
*
* http://docs.jquery.com/UI/Accordion#theming
*/
/* IE/Win - Fix animation bug - #4615 */
.ui-accordion { width: 100%; }
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
.ui-accordion .ui-accordion-li-fix { display: inline; }
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
.ui-accordion .ui-accordion-content-active { display: block; }

16
src/Test/Test/Content/themes/base/jquery.ui.all.css

@ -0,0 +1,16 @@
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI CSS Framework 1.8.11
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
*
* http://docs.jquery.com/UI/Theming
*/
@import "jquery.ui.base.css";
@import "jquery.ui.theme.css";

62
src/Test/Test/Content/themes/base/jquery.ui.autocomplete.css

@ -0,0 +1,62 @@
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI Autocomplete 1.8.11
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* http://docs.jquery.com/UI/Autocomplete#theming
*/
.ui-autocomplete { position: absolute; cursor: default; }
/* workarounds */
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI Menu 1.8.11
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
*
* http://docs.jquery.com/UI/Menu#theming
*/
.ui-menu {
list-style:none;
padding: 2px;
margin: 0;
display:block;
float: left;
}
.ui-menu .ui-menu {
margin-top: -3px;
}
.ui-menu .ui-menu-item {
margin:0;
padding: 0;
zoom: 1;
float: left;
clear: left;
width: 100%;
}
.ui-menu .ui-menu-item a {
text-decoration:none;
display:block;
padding:.2em .4em;
line-height:1.5;
zoom:1;
}
.ui-menu .ui-menu-item a.ui-state-hover,
.ui-menu .ui-menu-item a.ui-state-active {
font-weight: normal;
margin: -1px;
}

11
src/Test/Test/Content/themes/base/jquery.ui.base.css

@ -0,0 +1,11 @@
@import url("jquery.ui.core.css");
@import url("jquery.ui.resizable.css");
@import url("jquery.ui.selectable.css");
@import url("jquery.ui.accordion.css");
@import url("jquery.ui.autocomplete.css");
@import url("jquery.ui.button.css");
@import url("jquery.ui.dialog.css");
@import url("jquery.ui.slider.css");
@import url("jquery.ui.tabs.css");
@import url("jquery.ui.datepicker.css");
@import url("jquery.ui.progressbar.css");

43
src/Test/Test/Content/themes/base/jquery.ui.button.css

@ -0,0 +1,43 @@
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI Button 1.8.11
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
*
* http://docs.jquery.com/UI/Button#theming
*/
.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
.ui-button-icons-only { width: 3.4em; }
button.ui-button-icons-only { width: 3.7em; }
/*button text element */
.ui-button .ui-button-text { display: block; line-height: 1.4; }
.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; }
/* no icon support for input elements, provide padding by default */
input.ui-button { padding: .4em 1em; }
/*button icon element(s) */
.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-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
/*button sets*/
.ui-buttonset { margin-right: 7px; }
.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
/* workarounds */
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */

46
src/Test/Test/Content/themes/base/jquery.ui.core.css

@ -0,0 +1,46 @@
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI CSS Framework 1.8.11
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
*
* http://docs.jquery.com/UI/Theming/API
*/
/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,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:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
.ui-helper-clearfix { display: inline-block; }
/* required comment for clearfix to work in Opera \*/
* html .ui-helper-clearfix { height:1%; }
.ui-helper-clearfix { display:block; }
/* end clearfix */
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
/* Interaction Cues
----------------------------------*/
.ui-state-disabled { cursor: default !important; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }

73
src/Test/Test/Content/themes/base/jquery.ui.datepicker.css

@ -0,0 +1,73 @@
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI Datepicker 1.8.11
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
*
* http://docs.jquery.com/UI/Datepicker#theming
*/
.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-year {width: 100%;}
.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: bold; 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 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 .6em; width:auto; overflow:visible; }
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
/* with multiple calendars */
.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 { border-left-width:0; }
.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%; }
/* RTL support */
.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 { float:right; }
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
.ui-datepicker-cover {
display: none; /*sorry for IE5*/
display/**/: block; /*sorry for IE5*/
position: absolute; /*must have*/
z-index: -1; /*must have*/
filter: mask(); /*must have*/
top: -4px; /*must have*/
left: -4px; /*must have*/
width: 200px; /*must have*/
height: 200px; /*must have*/
}

26
src/Test/Test/Content/themes/base/jquery.ui.dialog.css

@ -0,0 +1,26 @@
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI Dialog 1.8.11
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
*
* http://docs.jquery.com/UI/Dialog#theming
*/
.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; 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: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }

16
src/Test/Test/Content/themes/base/jquery.ui.progressbar.css

@ -0,0 +1,16 @@
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI Progressbar 1.8.11
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
*
* http://docs.jquery.com/UI/Progressbar#theming
*/
.ui-progressbar { height:2em; text-align: left; }
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }

25
src/Test/Test/Content/themes/base/jquery.ui.resizable.css

@ -0,0 +1,25 @@
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI Resizable 1.8.11
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)]
*
* http://docs.jquery.com/UI/Resizable#theming
*/
.ui-resizable { position: relative;}
.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; 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;}

15
src/Test/Test/Content/themes/base/jquery.ui.selectable.css

@ -0,0 +1,15 @@
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI Selectable 1.8.11
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
*
* http://docs.jquery.com/UI/Selectable#theming
*/
.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }

29
src/Test/Test/Content/themes/base/jquery.ui.slider.css

@ -0,0 +1,29 @@
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI Slider 1.8.11
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
*
* http://docs.jquery.com/UI/Slider#theming
*/
.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-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; }

23
src/Test/Test/Content/themes/base/jquery.ui.tabs.css

@ -0,0 +1,23 @@
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI Tabs 1.8.11
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
*
* http://docs.jquery.com/UI/Tabs#theming
*/
.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
.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: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
.ui-tabs .ui-tabs-hide { display: none !important; }

257
src/Test/Test/Content/themes/base/jquery.ui.theme.css

@ -0,0 +1,257 @@
/*
* Note: While Microsoft is not the author of this file, Microsoft is
* offering you a license subject to the terms of the Microsoft Software
* License Terms for Microsoft ASP.NET Model View Controller 3.
* Microsoft reserves all other rights. The notices below are provided
* for informational purposes only and are not the license terms under
* which Microsoft distributed this file.
*
* jQuery UI CSS Framework 1.8.11
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
*
* http://docs.jquery.com/UI/Theming/API
*
* To view and modify this theme, visit http://jqueryui.com/themeroller/
*/
/* Component containers
----------------------------------*/
.ui-widget { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1.1em/*{fsDefault}*/; }
.ui-widget .ui-widget { font-size: 1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1em; }
.ui-widget-content { border: 1px solid #aaaaaa/*{borderColorContent}*/; background: #ffffff/*{bgColorContent}*/ url(images/ui-bg_flat_75_ffffff_40x100.png)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/; color: #222222/*{fcContent}*/; }
.ui-widget-content a { color: #222222/*{fcContent}*/; }
.ui-widget-header { border: 1px solid #aaaaaa/*{borderColorHeader}*/; background: #cccccc/*{bgColorHeader}*/ url(images/ui-bg_highlight-soft_75_cccccc_1x100.png)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/; color: #222222/*{fcHeader}*/; font-weight: bold; }
.ui-widget-header a { color: #222222/*{fcHeader}*/; }
/* Interaction states
----------------------------------*/
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3/*{borderColorDefault}*/; background: #e6e6e6/*{bgColorDefault}*/ url(images/ui-bg_glass_75_e6e6e6_1x400.png)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #555555/*{fcDefault}*/; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555/*{fcDefault}*/; text-decoration: none; }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999/*{borderColorHover}*/; background: #dadada/*{bgColorHover}*/ url(images/ui-bg_glass_75_dadada_1x400.png)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcHover}*/; }
.ui-state-hover a, .ui-state-hover a:hover { color: #212121/*{fcHover}*/; text-decoration: none; }
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa/*{borderColorActive}*/; background: #ffffff/*{bgColorActive}*/ url(images/ui-bg_glass_65_ffffff_1x400.png)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcActive}*/; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121/*{fcActive}*/; text-decoration: none; }
.ui-widget :active { outline: none; }
/* Interaction Cues
----------------------------------*/
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1/*{borderColorHighlight}*/; background: #fbf9ee/*{bgColorHighlight}*/ url(images/ui-bg_glass_55_fbf9ee_1x400.png)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/; color: #363636/*{fcHighlight}*/; }
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636/*{fcHighlight}*/; }
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a/*{borderColorError}*/; background: #fef1ec/*{bgColorError}*/ url(images/ui-bg_glass_95_fef1ec_1x400.png)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/; color: #cd0a0a/*{fcError}*/; }
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a/*{fcError}*/; }
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a/*{fcError}*/; }
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; }
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; }
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsHeader}*/; }
.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png)/*{iconsDefault}*/; }
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsHover}*/; }
.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsActive}*/; }
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png)/*{iconsHighlight}*/; }
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png)/*{iconsError}*/; }
/* positioning */
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-off { background-position: -96px -144px; }
.ui-icon-radio-on { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------*/
/* Corner radius */
.ui-corner-tl { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; }
.ui-corner-tr { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; }
.ui-corner-bl { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; }
.ui-corner-br { -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; }
.ui-corner-top { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; }
.ui-corner-bottom { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; }
.ui-corner-right { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; }
.ui-corner-left { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; }
.ui-corner-all { -moz-border-radius: 4px/*{cornerRadius}*/; -webkit-border-radius: 4px/*{cornerRadius}*/; border-radius: 4px/*{cornerRadius}*/; }
/* Overlays */
.ui-widget-overlay { background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityOverlay}*/; }
.ui-widget-shadow { margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/; padding: 8px/*{thicknessShadow}*/; background: #aaaaaa/*{bgColorShadow}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityShadow}*/; -moz-border-radius: 8px/*{cornerRadiusShadow}*/; -webkit-border-radius: 8px/*{cornerRadiusShadow}*/; border-radius: 8px/*{cornerRadiusShadow}*/; }

193
src/Test/Test/Controllers/AccountController.cs

@ -0,0 +1,193 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using Test.Models;
namespace Test.Controllers
{
public class AccountController : Controller
{
//
// GET: /Account/LogOn
public ActionResult LogOn()
{
return View();
}
//
// POST: /Account/LogOn
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/LogOff
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
//
// GET: /Account/Register
public ActionResult Register()
{
return View();
}
//
// POST: /Account/Register
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
MembershipCreateStatus createStatus;
Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus);
if (createStatus == MembershipCreateStatus.Success)
{
FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", ErrorCodeToString(createStatus));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/ChangePassword
[Authorize]
public ActionResult ChangePassword()
{
return View();
}
//
// POST: /Account/ChangePassword
[Authorize]
[HttpPost]
public ActionResult ChangePassword(ChangePasswordModel model)
{
if (ModelState.IsValid)
{
// ChangePassword will throw an exception rather
// than return false in certain failure scenarios.
bool changePasswordSucceeded;
try
{
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword);
}
catch (Exception)
{
changePasswordSucceeded = false;
}
if (changePasswordSucceeded)
{
return RedirectToAction("ChangePasswordSuccess");
}
else
{
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/ChangePasswordSuccess
public ActionResult ChangePasswordSuccess()
{
return View();
}
#region Status Codes
private static string ErrorCodeToString(MembershipCreateStatus createStatus)
{
// See http://go.microsoft.com/fwlink/?LinkID=177550 for
// a full list of status codes.
switch (createStatus)
{
case MembershipCreateStatus.DuplicateUserName:
return "User name already exists. Please enter a different user name.";
case MembershipCreateStatus.DuplicateEmail:
return "A user name for that e-mail address already exists. Please enter a different e-mail address.";
case MembershipCreateStatus.InvalidPassword:
return "The password provided is invalid. Please enter a valid password value.";
case MembershipCreateStatus.InvalidEmail:
return "The e-mail address provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidAnswer:
return "The password retrieval answer provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidQuestion:
return "The password retrieval question provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidUserName:
return "The user name provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.ProviderError:
return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
case MembershipCreateStatus.UserRejected:
return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
default:
return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
}
}
#endregion
}
}

48
src/Test/Test/Controllers/HomeController.cs

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Test.Controllers
{
using System.IO;
using System.Threading.Tasks;
using System.Web.Hosting;
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
List<string> images = new List<string>();
const string Path = "/images/";
string folder = HostingEnvironment.MapPath(Path);
if (folder != null)
{
DirectoryInfo directoryInfo = new DirectoryInfo(folder);
if (directoryInfo.Exists)
{
// Get all the files in the cache ordered by LastAccessTime - oldest first.
List<FileInfo> fileInfos = directoryInfo.EnumerateFiles("*", SearchOption.AllDirectories).OrderBy(x => x.LastAccessTime).ToList();
int counter = fileInfos.Count;
Parallel.ForEach(
fileInfos,
fileInfo => images.Add(Path + fileInfo.Name));
}
}
return View(images);
}
}
}

1
src/Test/Test/Global.asax

@ -0,0 +1 @@
<%@ Application Codebehind="Global.asax.cs" Inherits="Test.MvcApplication" Language="C#" %>

40
src/Test/Test/Global.asax.cs

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace Test
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
}

BIN
src/Test/Test/Images/1182076_e8c402e938_z.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

1
src/Test/Test/Images/6287131503_9b1f82e4f0_b.jpg.REMOVED.git-id

@ -0,0 +1 @@
4d040d9aa3519b3d2303419d1f03eebebf88e956

1
src/Test/Test/Images/Chrysanthemum.jpg.REMOVED.git-id

@ -0,0 +1 @@
757c2a628dd03b1cbe4b3ef07c153897a702b57a

1
src/Test/Test/Images/Chrysanthemum.png.REMOVED.git-id

@ -0,0 +1 @@
acbb9eee53e909814665053e0713335921c7d205

1
src/Test/Test/Images/Desert.jpg.REMOVED.git-id

@ -0,0 +1 @@
0b88c91336ff8073f34d21ccd683a01f0e0995da

1
src/Test/Test/Images/Hydrangeas.jpg.REMOVED.git-id

@ -0,0 +1 @@
a587c9656db0f4773fbada4807b9377df2216d77

1
src/Test/Test/Images/Jellyfish.jpg.REMOVED.git-id

@ -0,0 +1 @@
fa4fd4110616804c956a8fe4c388700ec4408eef

1
src/Test/Test/Images/Koala.jpg.REMOVED.git-id

@ -0,0 +1 @@
78704a099bad91c76ecb96417137464b0fa96b28

1
src/Test/Test/Images/Lighthouse.jpg.REMOVED.git-id

@ -0,0 +1 @@
494be09b8b43cfce7550ba6d54ff3cf799e4c315

1
src/Test/Test/Images/MSwanson - Wide Large - Rock 02.jpg.REMOVED.git-id

@ -0,0 +1 @@
33b6912af301bf216ee81d82b2c3ce6c49e03021

1
src/Test/Test/Images/Penguins.jpg.REMOVED.git-id

@ -0,0 +1 @@
030ab8a685bebb796c24cc710edd9e69859164f6

1
src/Test/Test/Images/Tulips.jpg.REMOVED.git-id

@ -0,0 +1 @@
54c51eb6a86f31a42433b8167470fb18dad32c7d

1
src/Test/Test/Images/fid11246.jpg.REMOVED.git-id

@ -0,0 +1 @@
30b51f2b174d67995deb595343e3cef4483d64e4

1
src/Test/Test/Images/fid9141.jpg.REMOVED.git-id

@ -0,0 +1 @@
f945a806925fa3a763bfd4ec421134c217c46494

1
src/Test/Test/Images/war_horse_quad.jpg.REMOVED.git-id

@ -0,0 +1 @@
c274f9d59c7f206c1cdf0cf97fd0cc13fc443191

67
src/Test/Test/Models/AccountModels.cs

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Web.Mvc;
using System.Web.Security;
namespace Test.Models
{
public class ChangePasswordModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class LogOnModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
public class RegisterModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email address")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
}

35
src/Test/Test/Properties/AssemblyInfo.cs

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Test")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Test")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("34f4c859-67cc-40d2-97ae-27e8c7157052")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

1
src/Test/Test/Scripts/MicrosoftAjax.debug.js.REMOVED.git-id

@ -0,0 +1 @@
a5f7942ef2b6b06e3c1aac2110fe7e5a1d88bf51

1
src/Test/Test/Scripts/MicrosoftAjax.js.REMOVED.git-id

@ -0,0 +1 @@
52e6626a072ad18c35926f81b94995a0c5eeee31

408
src/Test/Test/Scripts/MicrosoftMvcAjax.debug.js

@ -0,0 +1,408 @@
//!----------------------------------------------------------
//! Copyright (C) Microsoft Corporation. All rights reserved.
//!----------------------------------------------------------
//! MicrosoftMvcAjax.js
Type.registerNamespace('Sys.Mvc');
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.AjaxOptions
Sys.Mvc.$create_AjaxOptions = function Sys_Mvc_AjaxOptions() { return {}; }
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.InsertionMode
Sys.Mvc.InsertionMode = function() {
/// <field name="replace" type="Number" integer="true" static="true">
/// </field>
/// <field name="insertBefore" type="Number" integer="true" static="true">
/// </field>
/// <field name="insertAfter" type="Number" integer="true" static="true">
/// </field>
};
Sys.Mvc.InsertionMode.prototype = {
replace: 0,
insertBefore: 1,
insertAfter: 2
}
Sys.Mvc.InsertionMode.registerEnum('Sys.Mvc.InsertionMode', false);
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.AjaxContext
Sys.Mvc.AjaxContext = function Sys_Mvc_AjaxContext(request, updateTarget, loadingElement, insertionMode) {
/// <param name="request" type="Sys.Net.WebRequest">
/// </param>
/// <param name="updateTarget" type="Object" domElement="true">
/// </param>
/// <param name="loadingElement" type="Object" domElement="true">
/// </param>
/// <param name="insertionMode" type="Sys.Mvc.InsertionMode">
/// </param>
/// <field name="_insertionMode" type="Sys.Mvc.InsertionMode">
/// </field>
/// <field name="_loadingElement" type="Object" domElement="true">
/// </field>
/// <field name="_response" type="Sys.Net.WebRequestExecutor">
/// </field>
/// <field name="_request" type="Sys.Net.WebRequest">
/// </field>
/// <field name="_updateTarget" type="Object" domElement="true">
/// </field>
this._request = request;
this._updateTarget = updateTarget;
this._loadingElement = loadingElement;
this._insertionMode = insertionMode;
}
Sys.Mvc.AjaxContext.prototype = {
_insertionMode: 0,
_loadingElement: null,
_response: null,
_request: null,
_updateTarget: null,
get_data: function Sys_Mvc_AjaxContext$get_data() {
/// <value type="String"></value>
if (this._response) {
return this._response.get_responseData();
}
else {
return null;
}
},
get_insertionMode: function Sys_Mvc_AjaxContext$get_insertionMode() {
/// <value type="Sys.Mvc.InsertionMode"></value>
return this._insertionMode;
},
get_loadingElement: function Sys_Mvc_AjaxContext$get_loadingElement() {
/// <value type="Object" domElement="true"></value>
return this._loadingElement;
},
get_object: function Sys_Mvc_AjaxContext$get_object() {
/// <value type="Object"></value>
var executor = this.get_response();
return (executor) ? executor.get_object() : null;
},
get_response: function Sys_Mvc_AjaxContext$get_response() {
/// <value type="Sys.Net.WebRequestExecutor"></value>
return this._response;
},
set_response: function Sys_Mvc_AjaxContext$set_response(value) {
/// <value type="Sys.Net.WebRequestExecutor"></value>
this._response = value;
return value;
},
get_request: function Sys_Mvc_AjaxContext$get_request() {
/// <value type="Sys.Net.WebRequest"></value>
return this._request;
},
get_updateTarget: function Sys_Mvc_AjaxContext$get_updateTarget() {
/// <value type="Object" domElement="true"></value>
return this._updateTarget;
}
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.AsyncHyperlink
Sys.Mvc.AsyncHyperlink = function Sys_Mvc_AsyncHyperlink() {
}
Sys.Mvc.AsyncHyperlink.handleClick = function Sys_Mvc_AsyncHyperlink$handleClick(anchor, evt, ajaxOptions) {
/// <param name="anchor" type="Object" domElement="true">
/// </param>
/// <param name="evt" type="Sys.UI.DomEvent">
/// </param>
/// <param name="ajaxOptions" type="Sys.Mvc.AjaxOptions">
/// </param>
evt.preventDefault();
Sys.Mvc.MvcHelpers._asyncRequest(anchor.href, 'post', '', anchor, ajaxOptions);
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.MvcHelpers
Sys.Mvc.MvcHelpers = function Sys_Mvc_MvcHelpers() {
}
Sys.Mvc.MvcHelpers._serializeSubmitButton = function Sys_Mvc_MvcHelpers$_serializeSubmitButton(element, offsetX, offsetY) {
/// <param name="element" type="Object" domElement="true">
/// </param>
/// <param name="offsetX" type="Number" integer="true">
/// </param>
/// <param name="offsetY" type="Number" integer="true">
/// </param>
/// <returns type="String"></returns>
if (element.disabled) {
return null;
}
var name = element.name;
if (name) {
var tagName = element.tagName.toUpperCase();
var encodedName = encodeURIComponent(name);
var inputElement = element;
if (tagName === 'INPUT') {
var type = inputElement.type;
if (type === 'submit') {
return encodedName + '=' + encodeURIComponent(inputElement.value);
}
else if (type === 'image') {
return encodedName + '.x=' + offsetX + '&' + encodedName + '.y=' + offsetY;
}
}
else if ((tagName === 'BUTTON') && (name.length) && (inputElement.type === 'submit')) {
return encodedName + '=' + encodeURIComponent(inputElement.value);
}
}
return null;
}
Sys.Mvc.MvcHelpers._serializeForm = function Sys_Mvc_MvcHelpers$_serializeForm(form) {
/// <param name="form" type="Object" domElement="true">
/// </param>
/// <returns type="String"></returns>
var formElements = form.elements;
var formBody = new Sys.StringBuilder();
var count = formElements.length;
for (var i = 0; i < count; i++) {
var element = formElements[i];
var name = element.name;
if (!name || !name.length) {
continue;
}
var tagName = element.tagName.toUpperCase();
if (tagName === 'INPUT') {
var inputElement = element;
var type = inputElement.type;
if ((type === 'text') || (type === 'password') || (type === 'hidden') || (((type === 'checkbox') || (type === 'radio')) && element.checked)) {
formBody.append(encodeURIComponent(name));
formBody.append('=');
formBody.append(encodeURIComponent(inputElement.value));
formBody.append('&');
}
}
else if (tagName === 'SELECT') {
var selectElement = element;
var optionCount = selectElement.options.length;
for (var j = 0; j < optionCount; j++) {
var optionElement = selectElement.options[j];
if (optionElement.selected) {
formBody.append(encodeURIComponent(name));
formBody.append('=');
formBody.append(encodeURIComponent(optionElement.value));
formBody.append('&');
}
}
}
else if (tagName === 'TEXTAREA') {
formBody.append(encodeURIComponent(name));
formBody.append('=');
formBody.append(encodeURIComponent((element.value)));
formBody.append('&');
}
}
var additionalInput = form._additionalInput;
if (additionalInput) {
formBody.append(additionalInput);
formBody.append('&');
}
return formBody.toString();
}
Sys.Mvc.MvcHelpers._asyncRequest = function Sys_Mvc_MvcHelpers$_asyncRequest(url, verb, body, triggerElement, ajaxOptions) {
/// <param name="url" type="String">
/// </param>
/// <param name="verb" type="String">
/// </param>
/// <param name="body" type="String">
/// </param>
/// <param name="triggerElement" type="Object" domElement="true">
/// </param>
/// <param name="ajaxOptions" type="Sys.Mvc.AjaxOptions">
/// </param>
if (ajaxOptions.confirm) {
if (!confirm(ajaxOptions.confirm)) {
return;
}
}
if (ajaxOptions.url) {
url = ajaxOptions.url;
}
if (ajaxOptions.httpMethod) {
verb = ajaxOptions.httpMethod;
}
if (body.length > 0 && !body.endsWith('&')) {
body += '&';
}
body += 'X-Requested-With=XMLHttpRequest';
var upperCaseVerb = verb.toUpperCase();
var isGetOrPost = (upperCaseVerb === 'GET' || upperCaseVerb === 'POST');
if (!isGetOrPost) {
body += '&';
body += 'X-HTTP-Method-Override=' + upperCaseVerb;
}
var requestBody = '';
if (upperCaseVerb === 'GET' || upperCaseVerb === 'DELETE') {
if (url.indexOf('?') > -1) {
if (!url.endsWith('&')) {
url += '&';
}
url += body;
}
else {
url += '?';
url += body;
}
}
else {
requestBody = body;
}
var request = new Sys.Net.WebRequest();
request.set_url(url);
if (isGetOrPost) {
request.set_httpVerb(verb);
}
else {
request.set_httpVerb('POST');
request.get_headers()['X-HTTP-Method-Override'] = upperCaseVerb;
}
request.set_body(requestBody);
if (verb.toUpperCase() === 'PUT') {
request.get_headers()['Content-Type'] = 'application/x-www-form-urlencoded;';
}
request.get_headers()['X-Requested-With'] = 'XMLHttpRequest';
var updateElement = null;
if (ajaxOptions.updateTargetId) {
updateElement = $get(ajaxOptions.updateTargetId);
}
var loadingElement = null;
if (ajaxOptions.loadingElementId) {
loadingElement = $get(ajaxOptions.loadingElementId);
}
var ajaxContext = new Sys.Mvc.AjaxContext(request, updateElement, loadingElement, ajaxOptions.insertionMode);
var continueRequest = true;
if (ajaxOptions.onBegin) {
continueRequest = ajaxOptions.onBegin(ajaxContext) !== false;
}
if (loadingElement) {
Sys.UI.DomElement.setVisible(ajaxContext.get_loadingElement(), true);
}
if (continueRequest) {
request.add_completed(Function.createDelegate(null, function(executor) {
Sys.Mvc.MvcHelpers._onComplete(request, ajaxOptions, ajaxContext);
}));
request.invoke();
}
}
Sys.Mvc.MvcHelpers._onComplete = function Sys_Mvc_MvcHelpers$_onComplete(request, ajaxOptions, ajaxContext) {
/// <param name="request" type="Sys.Net.WebRequest">
/// </param>
/// <param name="ajaxOptions" type="Sys.Mvc.AjaxOptions">
/// </param>
/// <param name="ajaxContext" type="Sys.Mvc.AjaxContext">
/// </param>
ajaxContext.set_response(request.get_executor());
if (ajaxOptions.onComplete && ajaxOptions.onComplete(ajaxContext) === false) {
return;
}
var statusCode = ajaxContext.get_response().get_statusCode();
if ((statusCode >= 200 && statusCode < 300) || statusCode === 304 || statusCode === 1223) {
if (statusCode !== 204 && statusCode !== 304 && statusCode !== 1223) {
var contentType = ajaxContext.get_response().getResponseHeader('Content-Type');
if ((contentType) && (contentType.indexOf('application/x-javascript') !== -1)) {
eval(ajaxContext.get_data());
}
else {
Sys.Mvc.MvcHelpers.updateDomElement(ajaxContext.get_updateTarget(), ajaxContext.get_insertionMode(), ajaxContext.get_data());
}
}
if (ajaxOptions.onSuccess) {
ajaxOptions.onSuccess(ajaxContext);
}
}
else {
if (ajaxOptions.onFailure) {
ajaxOptions.onFailure(ajaxContext);
}
}
if (ajaxContext.get_loadingElement()) {
Sys.UI.DomElement.setVisible(ajaxContext.get_loadingElement(), false);
}
}
Sys.Mvc.MvcHelpers.updateDomElement = function Sys_Mvc_MvcHelpers$updateDomElement(target, insertionMode, content) {
/// <param name="target" type="Object" domElement="true">
/// </param>
/// <param name="insertionMode" type="Sys.Mvc.InsertionMode">
/// </param>
/// <param name="content" type="String">
/// </param>
if (target) {
switch (insertionMode) {
case Sys.Mvc.InsertionMode.replace:
target.innerHTML = content;
break;
case Sys.Mvc.InsertionMode.insertBefore:
if (content && content.length > 0) {
target.innerHTML = content + target.innerHTML.trimStart();
}
break;
case Sys.Mvc.InsertionMode.insertAfter:
if (content && content.length > 0) {
target.innerHTML = target.innerHTML.trimEnd() + content;
}
break;
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.AsyncForm
Sys.Mvc.AsyncForm = function Sys_Mvc_AsyncForm() {
}
Sys.Mvc.AsyncForm.handleClick = function Sys_Mvc_AsyncForm$handleClick(form, evt) {
/// <param name="form" type="Object" domElement="true">
/// </param>
/// <param name="evt" type="Sys.UI.DomEvent">
/// </param>
var additionalInput = Sys.Mvc.MvcHelpers._serializeSubmitButton(evt.target, evt.offsetX, evt.offsetY);
form._additionalInput = additionalInput;
}
Sys.Mvc.AsyncForm.handleSubmit = function Sys_Mvc_AsyncForm$handleSubmit(form, evt, ajaxOptions) {
/// <param name="form" type="Object" domElement="true">
/// </param>
/// <param name="evt" type="Sys.UI.DomEvent">
/// </param>
/// <param name="ajaxOptions" type="Sys.Mvc.AjaxOptions">
/// </param>
evt.preventDefault();
var validationCallbacks = form.validationCallbacks;
if (validationCallbacks) {
for (var i = 0; i < validationCallbacks.length; i++) {
var callback = validationCallbacks[i];
if (!callback()) {
return;
}
}
}
var body = Sys.Mvc.MvcHelpers._serializeForm(form);
Sys.Mvc.MvcHelpers._asyncRequest(form.action, form.method || 'post', body, form, ajaxOptions);
}
Sys.Mvc.AjaxContext.registerClass('Sys.Mvc.AjaxContext');
Sys.Mvc.AsyncHyperlink.registerClass('Sys.Mvc.AsyncHyperlink');
Sys.Mvc.MvcHelpers.registerClass('Sys.Mvc.MvcHelpers');
Sys.Mvc.AsyncForm.registerClass('Sys.Mvc.AsyncForm');
// ---- Do not remove this footer ----
// Generated using Script# v0.5.0.0 (http://projects.nikhilk.net)
// -----------------------------------

25
src/Test/Test/Scripts/MicrosoftMvcAjax.js

@ -0,0 +1,25 @@
//----------------------------------------------------------
// Copyright (C) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------
// MicrosoftMvcAjax.js
Type.registerNamespace('Sys.Mvc');Sys.Mvc.$create_AjaxOptions=function(){return {};}
Sys.Mvc.InsertionMode=function(){};Sys.Mvc.InsertionMode.prototype = {replace:0,insertBefore:1,insertAfter:2}
Sys.Mvc.InsertionMode.registerEnum('Sys.Mvc.InsertionMode',false);Sys.Mvc.AjaxContext=function(request,updateTarget,loadingElement,insertionMode){this.$3=request;this.$4=updateTarget;this.$1=loadingElement;this.$0=insertionMode;}
Sys.Mvc.AjaxContext.prototype={$0:0,$1:null,$2:null,$3:null,$4:null,get_data:function(){if(this.$2){return this.$2.get_responseData();}else{return null;}},get_insertionMode:function(){return this.$0;},get_loadingElement:function(){return this.$1;},get_object:function(){var $0=this.get_response();return ($0)?$0.get_object():null;},get_response:function(){return this.$2;},set_response:function(value){this.$2=value;return value;},get_request:function(){return this.$3;},get_updateTarget:function(){return this.$4;}}
Sys.Mvc.AsyncHyperlink=function(){}
Sys.Mvc.AsyncHyperlink.handleClick=function(anchor,evt,ajaxOptions){evt.preventDefault();Sys.Mvc.MvcHelpers.$2(anchor.href,'post','',anchor,ajaxOptions);}
Sys.Mvc.MvcHelpers=function(){}
Sys.Mvc.MvcHelpers.$0=function($p0,$p1,$p2){if($p0.disabled){return null;}var $0=$p0.name;if($0){var $1=$p0.tagName.toUpperCase();var $2=encodeURIComponent($0);var $3=$p0;if($1==='INPUT'){var $4=$3.type;if($4==='submit'){return $2+'='+encodeURIComponent($3.value);}else if($4==='image'){return $2+'.x='+$p1+'&'+$2+'.y='+$p2;}}else if(($1==='BUTTON')&&($0.length)&&($3.type==='submit')){return $2+'='+encodeURIComponent($3.value);}}return null;}
Sys.Mvc.MvcHelpers.$1=function($p0){var $0=$p0.elements;var $1=new Sys.StringBuilder();var $2=$0.length;for(var $4=0;$4<$2;$4++){var $5=$0[$4];var $6=$5.name;if(!$6||!$6.length){continue;}var $7=$5.tagName.toUpperCase();if($7==='INPUT'){var $8=$5;var $9=$8.type;if(($9==='text')||($9==='password')||($9==='hidden')||((($9==='checkbox')||($9==='radio'))&&$5.checked)){$1.append(encodeURIComponent($6));$1.append('=');$1.append(encodeURIComponent($8.value));$1.append('&');}}else if($7==='SELECT'){var $A=$5;var $B=$A.options.length;for(var $C=0;$C<$B;$C++){var $D=$A.options[$C];if($D.selected){$1.append(encodeURIComponent($6));$1.append('=');$1.append(encodeURIComponent($D.value));$1.append('&');}}}else if($7==='TEXTAREA'){$1.append(encodeURIComponent($6));$1.append('=');$1.append(encodeURIComponent(($5.value)));$1.append('&');}}var $3=$p0._additionalInput;if($3){$1.append($3);$1.append('&');}return $1.toString();}
Sys.Mvc.MvcHelpers.$2=function($p0,$p1,$p2,$p3,$p4){if($p4.confirm){if(!confirm($p4.confirm)){return;}}if($p4.url){$p0=$p4.url;}if($p4.httpMethod){$p1=$p4.httpMethod;}if($p2.length>0&&!$p2.endsWith('&')){$p2+='&';}$p2+='X-Requested-With=XMLHttpRequest';var $0=$p1.toUpperCase();var $1=($0==='GET'||$0==='POST');if(!$1){$p2+='&';$p2+='X-HTTP-Method-Override='+$0;}var $2='';if($0==='GET'||$0==='DELETE'){if($p0.indexOf('?')>-1){if(!$p0.endsWith('&')){$p0+='&';}$p0+=$p2;}else{$p0+='?';$p0+=$p2;}}else{$2=$p2;}var $3=new Sys.Net.WebRequest();$3.set_url($p0);if($1){$3.set_httpVerb($p1);}else{$3.set_httpVerb('POST');$3.get_headers()['X-HTTP-Method-Override']=$0;}$3.set_body($2);if($p1.toUpperCase()==='PUT'){$3.get_headers()['Content-Type']='application/x-www-form-urlencoded;';}$3.get_headers()['X-Requested-With']='XMLHttpRequest';var $4=null;if($p4.updateTargetId){$4=$get($p4.updateTargetId);}var $5=null;if($p4.loadingElementId){$5=$get($p4.loadingElementId);}var $6=new Sys.Mvc.AjaxContext($3,$4,$5,$p4.insertionMode);var $7=true;if($p4.onBegin){$7=$p4.onBegin($6)!==false;}if($5){Sys.UI.DomElement.setVisible($6.get_loadingElement(),true);}if($7){$3.add_completed(Function.createDelegate(null,function($p1_0){
Sys.Mvc.MvcHelpers.$3($3,$p4,$6);}));$3.invoke();}}
Sys.Mvc.MvcHelpers.$3=function($p0,$p1,$p2){$p2.set_response($p0.get_executor());if($p1.onComplete&&$p1.onComplete($p2)===false){return;}var $0=$p2.get_response().get_statusCode();if(($0>=200&&$0<300)||$0===304||$0===1223){if($0!==204&&$0!==304&&$0!==1223){var $1=$p2.get_response().getResponseHeader('Content-Type');if(($1)&&($1.indexOf('application/x-javascript')!==-1)){eval($p2.get_data());}else{Sys.Mvc.MvcHelpers.updateDomElement($p2.get_updateTarget(),$p2.get_insertionMode(),$p2.get_data());}}if($p1.onSuccess){$p1.onSuccess($p2);}}else{if($p1.onFailure){$p1.onFailure($p2);}}if($p2.get_loadingElement()){Sys.UI.DomElement.setVisible($p2.get_loadingElement(),false);}}
Sys.Mvc.MvcHelpers.updateDomElement=function(target,insertionMode,content){if(target){switch(insertionMode){case 0:target.innerHTML=content;break;case 1:if(content&&content.length>0){target.innerHTML=content+target.innerHTML.trimStart();}break;case 2:if(content&&content.length>0){target.innerHTML=target.innerHTML.trimEnd()+content;}break;}}}
Sys.Mvc.AsyncForm=function(){}
Sys.Mvc.AsyncForm.handleClick=function(form,evt){var $0=Sys.Mvc.MvcHelpers.$0(evt.target,evt.offsetX,evt.offsetY);form._additionalInput = $0;}
Sys.Mvc.AsyncForm.handleSubmit=function(form,evt,ajaxOptions){evt.preventDefault();var $0=form.validationCallbacks;if($0){for(var $2=0;$2<$0.length;$2++){var $3=$0[$2];if(!$3()){return;}}}var $1=Sys.Mvc.MvcHelpers.$1(form);Sys.Mvc.MvcHelpers.$2(form.action,form.method||'post',$1,form,ajaxOptions);}
Sys.Mvc.AjaxContext.registerClass('Sys.Mvc.AjaxContext');Sys.Mvc.AsyncHyperlink.registerClass('Sys.Mvc.AsyncHyperlink');Sys.Mvc.MvcHelpers.registerClass('Sys.Mvc.MvcHelpers');Sys.Mvc.AsyncForm.registerClass('Sys.Mvc.AsyncForm');
// ---- Do not remove this footer ----
// Generated using Script# v0.5.0.0 (http://projects.nikhilk.net)
// -----------------------------------

883
src/Test/Test/Scripts/MicrosoftMvcValidation.debug.js

@ -0,0 +1,883 @@
//!----------------------------------------------------------
//! Copyright (C) Microsoft Corporation. All rights reserved.
//!----------------------------------------------------------
//! MicrosoftMvcValidation.js
Type.registerNamespace('Sys.Mvc');
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.Validation
Sys.Mvc.$create_Validation = function Sys_Mvc_Validation() { return {}; }
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.JsonValidationField
Sys.Mvc.$create_JsonValidationField = function Sys_Mvc_JsonValidationField() { return {}; }
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.JsonValidationOptions
Sys.Mvc.$create_JsonValidationOptions = function Sys_Mvc_JsonValidationOptions() { return {}; }
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.JsonValidationRule
Sys.Mvc.$create_JsonValidationRule = function Sys_Mvc_JsonValidationRule() { return {}; }
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.ValidationContext
Sys.Mvc.$create_ValidationContext = function Sys_Mvc_ValidationContext() { return {}; }
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.NumberValidator
Sys.Mvc.NumberValidator = function Sys_Mvc_NumberValidator() {
}
Sys.Mvc.NumberValidator.create = function Sys_Mvc_NumberValidator$create(rule) {
/// <param name="rule" type="Sys.Mvc.JsonValidationRule">
/// </param>
/// <returns type="Sys.Mvc.Validator"></returns>
return Function.createDelegate(new Sys.Mvc.NumberValidator(), new Sys.Mvc.NumberValidator().validate);
}
Sys.Mvc.NumberValidator.prototype = {
validate: function Sys_Mvc_NumberValidator$validate(value, context) {
/// <param name="value" type="String">
/// </param>
/// <param name="context" type="Sys.Mvc.ValidationContext">
/// </param>
/// <returns type="Object"></returns>
if (Sys.Mvc._validationUtil.stringIsNullOrEmpty(value)) {
return true;
}
var n = Number.parseLocale(value);
return (!isNaN(n));
}
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.FormContext
Sys.Mvc.FormContext = function Sys_Mvc_FormContext(formElement, validationSummaryElement) {
/// <param name="formElement" type="Object" domElement="true">
/// </param>
/// <param name="validationSummaryElement" type="Object" domElement="true">
/// </param>
/// <field name="_validationSummaryErrorCss" type="String" static="true">
/// </field>
/// <field name="_validationSummaryValidCss" type="String" static="true">
/// </field>
/// <field name="_formValidationTag" type="String" static="true">
/// </field>
/// <field name="_onClickHandler" type="Sys.UI.DomEventHandler">
/// </field>
/// <field name="_onSubmitHandler" type="Sys.UI.DomEventHandler">
/// </field>
/// <field name="_errors" type="Array">
/// </field>
/// <field name="_submitButtonClicked" type="Object" domElement="true">
/// </field>
/// <field name="_validationSummaryElement" type="Object" domElement="true">
/// </field>
/// <field name="_validationSummaryULElement" type="Object" domElement="true">
/// </field>
/// <field name="fields" type="Array" elementType="FieldContext">
/// </field>
/// <field name="_formElement" type="Object" domElement="true">
/// </field>
/// <field name="replaceValidationSummary" type="Boolean">
/// </field>
this._errors = [];
this.fields = new Array(0);
this._formElement = formElement;
this._validationSummaryElement = validationSummaryElement;
formElement[Sys.Mvc.FormContext._formValidationTag] = this;
if (validationSummaryElement) {
var ulElements = validationSummaryElement.getElementsByTagName('ul');
if (ulElements.length > 0) {
this._validationSummaryULElement = ulElements[0];
}
}
this._onClickHandler = Function.createDelegate(this, this._form_OnClick);
this._onSubmitHandler = Function.createDelegate(this, this._form_OnSubmit);
}
Sys.Mvc.FormContext._Application_Load = function Sys_Mvc_FormContext$_Application_Load() {
var allFormOptions = window.mvcClientValidationMetadata;
if (allFormOptions) {
while (allFormOptions.length > 0) {
var thisFormOptions = allFormOptions.pop();
Sys.Mvc.FormContext._parseJsonOptions(thisFormOptions);
}
}
}
Sys.Mvc.FormContext._getFormElementsWithName = function Sys_Mvc_FormContext$_getFormElementsWithName(formElement, name) {
/// <param name="formElement" type="Object" domElement="true">
/// </param>
/// <param name="name" type="String">
/// </param>
/// <returns type="Array" elementType="Object" elementDomElement="true"></returns>
var allElementsWithNameInForm = [];
var allElementsWithName = document.getElementsByName(name);
for (var i = 0; i < allElementsWithName.length; i++) {
var thisElement = allElementsWithName[i];
if (Sys.Mvc.FormContext._isElementInHierarchy(formElement, thisElement)) {
Array.add(allElementsWithNameInForm, thisElement);
}
}
return allElementsWithNameInForm;
}
Sys.Mvc.FormContext.getValidationForForm = function Sys_Mvc_FormContext$getValidationForForm(formElement) {
/// <param name="formElement" type="Object" domElement="true">
/// </param>
/// <returns type="Sys.Mvc.FormContext"></returns>
return formElement[Sys.Mvc.FormContext._formValidationTag];
}
Sys.Mvc.FormContext._isElementInHierarchy = function Sys_Mvc_FormContext$_isElementInHierarchy(parent, child) {
/// <param name="parent" type="Object" domElement="true">
/// </param>
/// <param name="child" type="Object" domElement="true">
/// </param>
/// <returns type="Boolean"></returns>
while (child) {
if (parent === child) {
return true;
}
child = child.parentNode;
}
return false;
}
Sys.Mvc.FormContext._parseJsonOptions = function Sys_Mvc_FormContext$_parseJsonOptions(options) {
/// <param name="options" type="Sys.Mvc.JsonValidationOptions">
/// </param>
/// <returns type="Sys.Mvc.FormContext"></returns>
var formElement = $get(options.FormId);
var validationSummaryElement = (!Sys.Mvc._validationUtil.stringIsNullOrEmpty(options.ValidationSummaryId)) ? $get(options.ValidationSummaryId) : null;
var formContext = new Sys.Mvc.FormContext(formElement, validationSummaryElement);
formContext.enableDynamicValidation();
formContext.replaceValidationSummary = options.ReplaceValidationSummary;
for (var i = 0; i < options.Fields.length; i++) {
var field = options.Fields[i];
var fieldElements = Sys.Mvc.FormContext._getFormElementsWithName(formElement, field.FieldName);
var validationMessageElement = (!Sys.Mvc._validationUtil.stringIsNullOrEmpty(field.ValidationMessageId)) ? $get(field.ValidationMessageId) : null;
var fieldContext = new Sys.Mvc.FieldContext(formContext);
Array.addRange(fieldContext.elements, fieldElements);
fieldContext.validationMessageElement = validationMessageElement;
fieldContext.replaceValidationMessageContents = field.ReplaceValidationMessageContents;
for (var j = 0; j < field.ValidationRules.length; j++) {
var rule = field.ValidationRules[j];
var validator = Sys.Mvc.ValidatorRegistry.getValidator(rule);
if (validator) {
var validation = Sys.Mvc.$create_Validation();
validation.fieldErrorMessage = rule.ErrorMessage;
validation.validator = validator;
Array.add(fieldContext.validations, validation);
}
}
fieldContext.enableDynamicValidation();
Array.add(formContext.fields, fieldContext);
}
var registeredValidatorCallbacks = formElement.validationCallbacks;
if (!registeredValidatorCallbacks) {
registeredValidatorCallbacks = [];
formElement.validationCallbacks = registeredValidatorCallbacks;
}
registeredValidatorCallbacks.push(Function.createDelegate(null, function() {
return Sys.Mvc._validationUtil.arrayIsNullOrEmpty(formContext.validate('submit'));
}));
return formContext;
}
Sys.Mvc.FormContext.prototype = {
_onClickHandler: null,
_onSubmitHandler: null,
_submitButtonClicked: null,
_validationSummaryElement: null,
_validationSummaryULElement: null,
_formElement: null,
replaceValidationSummary: false,
addError: function Sys_Mvc_FormContext$addError(message) {
/// <param name="message" type="String">
/// </param>
this.addErrors([ message ]);
},
addErrors: function Sys_Mvc_FormContext$addErrors(messages) {
/// <param name="messages" type="Array" elementType="String">
/// </param>
if (!Sys.Mvc._validationUtil.arrayIsNullOrEmpty(messages)) {
Array.addRange(this._errors, messages);
this._onErrorCountChanged();
}
},
clearErrors: function Sys_Mvc_FormContext$clearErrors() {
Array.clear(this._errors);
this._onErrorCountChanged();
},
_displayError: function Sys_Mvc_FormContext$_displayError() {
if (this._validationSummaryElement) {
if (this._validationSummaryULElement) {
Sys.Mvc._validationUtil.removeAllChildren(this._validationSummaryULElement);
for (var i = 0; i < this._errors.length; i++) {
var liElement = document.createElement('li');
Sys.Mvc._validationUtil.setInnerText(liElement, this._errors[i]);
this._validationSummaryULElement.appendChild(liElement);
}
}
Sys.UI.DomElement.removeCssClass(this._validationSummaryElement, Sys.Mvc.FormContext._validationSummaryValidCss);
Sys.UI.DomElement.addCssClass(this._validationSummaryElement, Sys.Mvc.FormContext._validationSummaryErrorCss);
}
},
_displaySuccess: function Sys_Mvc_FormContext$_displaySuccess() {
var validationSummaryElement = this._validationSummaryElement;
if (validationSummaryElement) {
var validationSummaryULElement = this._validationSummaryULElement;
if (validationSummaryULElement) {
validationSummaryULElement.innerHTML = '';
}
Sys.UI.DomElement.removeCssClass(validationSummaryElement, Sys.Mvc.FormContext._validationSummaryErrorCss);
Sys.UI.DomElement.addCssClass(validationSummaryElement, Sys.Mvc.FormContext._validationSummaryValidCss);
}
},
enableDynamicValidation: function Sys_Mvc_FormContext$enableDynamicValidation() {
Sys.UI.DomEvent.addHandler(this._formElement, 'click', this._onClickHandler);
Sys.UI.DomEvent.addHandler(this._formElement, 'submit', this._onSubmitHandler);
},
_findSubmitButton: function Sys_Mvc_FormContext$_findSubmitButton(element) {
/// <param name="element" type="Object" domElement="true">
/// </param>
/// <returns type="Object" domElement="true"></returns>
if (element.disabled) {
return null;
}
var tagName = element.tagName.toUpperCase();
var inputElement = element;
if (tagName === 'INPUT') {
var type = inputElement.type;
if (type === 'submit' || type === 'image') {
return inputElement;
}
}
else if ((tagName === 'BUTTON') && (inputElement.type === 'submit')) {
return inputElement;
}
return null;
},
_form_OnClick: function Sys_Mvc_FormContext$_form_OnClick(e) {
/// <param name="e" type="Sys.UI.DomEvent">
/// </param>
this._submitButtonClicked = this._findSubmitButton(e.target);
},
_form_OnSubmit: function Sys_Mvc_FormContext$_form_OnSubmit(e) {
/// <param name="e" type="Sys.UI.DomEvent">
/// </param>
var form = e.target;
var submitButton = this._submitButtonClicked;
if (submitButton && submitButton.disableValidation) {
return;
}
var errorMessages = this.validate('submit');
if (!Sys.Mvc._validationUtil.arrayIsNullOrEmpty(errorMessages)) {
e.preventDefault();
}
},
_onErrorCountChanged: function Sys_Mvc_FormContext$_onErrorCountChanged() {
if (!this._errors.length) {
this._displaySuccess();
}
else {
this._displayError();
}
},
validate: function Sys_Mvc_FormContext$validate(eventName) {
/// <param name="eventName" type="String">
/// </param>
/// <returns type="Array" elementType="String"></returns>
var fields = this.fields;
var errors = [];
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
if (!field.elements[0].disabled) {
var thisErrors = field.validate(eventName);
if (thisErrors) {
Array.addRange(errors, thisErrors);
}
}
}
if (this.replaceValidationSummary) {
this.clearErrors();
this.addErrors(errors);
}
return errors;
}
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.FieldContext
Sys.Mvc.FieldContext = function Sys_Mvc_FieldContext(formContext) {
/// <param name="formContext" type="Sys.Mvc.FormContext">
/// </param>
/// <field name="_hasTextChangedTag" type="String" static="true">
/// </field>
/// <field name="_hasValidationFiredTag" type="String" static="true">
/// </field>
/// <field name="_inputElementErrorCss" type="String" static="true">
/// </field>
/// <field name="_inputElementValidCss" type="String" static="true">
/// </field>
/// <field name="_validationMessageErrorCss" type="String" static="true">
/// </field>
/// <field name="_validationMessageValidCss" type="String" static="true">
/// </field>
/// <field name="_onBlurHandler" type="Sys.UI.DomEventHandler">
/// </field>
/// <field name="_onChangeHandler" type="Sys.UI.DomEventHandler">
/// </field>
/// <field name="_onInputHandler" type="Sys.UI.DomEventHandler">
/// </field>
/// <field name="_onPropertyChangeHandler" type="Sys.UI.DomEventHandler">
/// </field>
/// <field name="_errors" type="Array">
/// </field>
/// <field name="defaultErrorMessage" type="String">
/// </field>
/// <field name="elements" type="Array" elementType="Object" elementDomElement="true">
/// </field>
/// <field name="formContext" type="Sys.Mvc.FormContext">
/// </field>
/// <field name="replaceValidationMessageContents" type="Boolean">
/// </field>
/// <field name="validationMessageElement" type="Object" domElement="true">
/// </field>
/// <field name="validations" type="Array" elementType="Validation">
/// </field>
this._errors = [];
this.elements = new Array(0);
this.validations = new Array(0);
this.formContext = formContext;
this._onBlurHandler = Function.createDelegate(this, this._element_OnBlur);
this._onChangeHandler = Function.createDelegate(this, this._element_OnChange);
this._onInputHandler = Function.createDelegate(this, this._element_OnInput);
this._onPropertyChangeHandler = Function.createDelegate(this, this._element_OnPropertyChange);
}
Sys.Mvc.FieldContext.prototype = {
_onBlurHandler: null,
_onChangeHandler: null,
_onInputHandler: null,
_onPropertyChangeHandler: null,
defaultErrorMessage: null,
formContext: null,
replaceValidationMessageContents: false,
validationMessageElement: null,
addError: function Sys_Mvc_FieldContext$addError(message) {
/// <param name="message" type="String">
/// </param>
this.addErrors([ message ]);
},
addErrors: function Sys_Mvc_FieldContext$addErrors(messages) {
/// <param name="messages" type="Array" elementType="String">
/// </param>
if (!Sys.Mvc._validationUtil.arrayIsNullOrEmpty(messages)) {
Array.addRange(this._errors, messages);
this._onErrorCountChanged();
}
},
clearErrors: function Sys_Mvc_FieldContext$clearErrors() {
Array.clear(this._errors);
this._onErrorCountChanged();
},
_displayError: function Sys_Mvc_FieldContext$_displayError() {
var validationMessageElement = this.validationMessageElement;
if (validationMessageElement) {
if (this.replaceValidationMessageContents) {
Sys.Mvc._validationUtil.setInnerText(validationMessageElement, this._errors[0]);
}
Sys.UI.DomElement.removeCssClass(validationMessageElement, Sys.Mvc.FieldContext._validationMessageValidCss);
Sys.UI.DomElement.addCssClass(validationMessageElement, Sys.Mvc.FieldContext._validationMessageErrorCss);
}
var elements = this.elements;
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
Sys.UI.DomElement.removeCssClass(element, Sys.Mvc.FieldContext._inputElementValidCss);
Sys.UI.DomElement.addCssClass(element, Sys.Mvc.FieldContext._inputElementErrorCss);
}
},
_displaySuccess: function Sys_Mvc_FieldContext$_displaySuccess() {
var validationMessageElement = this.validationMessageElement;
if (validationMessageElement) {
if (this.replaceValidationMessageContents) {
Sys.Mvc._validationUtil.setInnerText(validationMessageElement, '');
}
Sys.UI.DomElement.removeCssClass(validationMessageElement, Sys.Mvc.FieldContext._validationMessageErrorCss);
Sys.UI.DomElement.addCssClass(validationMessageElement, Sys.Mvc.FieldContext._validationMessageValidCss);
}
var elements = this.elements;
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
Sys.UI.DomElement.removeCssClass(element, Sys.Mvc.FieldContext._inputElementErrorCss);
Sys.UI.DomElement.addCssClass(element, Sys.Mvc.FieldContext._inputElementValidCss);
}
},
_element_OnBlur: function Sys_Mvc_FieldContext$_element_OnBlur(e) {
/// <param name="e" type="Sys.UI.DomEvent">
/// </param>
if (e.target[Sys.Mvc.FieldContext._hasTextChangedTag] || e.target[Sys.Mvc.FieldContext._hasValidationFiredTag]) {
this.validate('blur');
}
},
_element_OnChange: function Sys_Mvc_FieldContext$_element_OnChange(e) {
/// <param name="e" type="Sys.UI.DomEvent">
/// </param>
e.target[Sys.Mvc.FieldContext._hasTextChangedTag] = true;
},
_element_OnInput: function Sys_Mvc_FieldContext$_element_OnInput(e) {
/// <param name="e" type="Sys.UI.DomEvent">
/// </param>
e.target[Sys.Mvc.FieldContext._hasTextChangedTag] = true;
if (e.target[Sys.Mvc.FieldContext._hasValidationFiredTag]) {
this.validate('input');
}
},
_element_OnPropertyChange: function Sys_Mvc_FieldContext$_element_OnPropertyChange(e) {
/// <param name="e" type="Sys.UI.DomEvent">
/// </param>
if (e.rawEvent.propertyName === 'value') {
e.target[Sys.Mvc.FieldContext._hasTextChangedTag] = true;
if (e.target[Sys.Mvc.FieldContext._hasValidationFiredTag]) {
this.validate('input');
}
}
},
enableDynamicValidation: function Sys_Mvc_FieldContext$enableDynamicValidation() {
var elements = this.elements;
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (Sys.Mvc._validationUtil.elementSupportsEvent(element, 'onpropertychange')) {
var compatMode = document.documentMode;
if (compatMode && compatMode >= 8) {
Sys.UI.DomEvent.addHandler(element, 'propertychange', this._onPropertyChangeHandler);
}
}
else {
Sys.UI.DomEvent.addHandler(element, 'input', this._onInputHandler);
}
Sys.UI.DomEvent.addHandler(element, 'change', this._onChangeHandler);
Sys.UI.DomEvent.addHandler(element, 'blur', this._onBlurHandler);
}
},
_getErrorString: function Sys_Mvc_FieldContext$_getErrorString(validatorReturnValue, fieldErrorMessage) {
/// <param name="validatorReturnValue" type="Object">
/// </param>
/// <param name="fieldErrorMessage" type="String">
/// </param>
/// <returns type="String"></returns>
var fallbackErrorMessage = fieldErrorMessage || this.defaultErrorMessage;
if (Boolean.isInstanceOfType(validatorReturnValue)) {
return (validatorReturnValue) ? null : fallbackErrorMessage;
}
if (String.isInstanceOfType(validatorReturnValue)) {
return ((validatorReturnValue).length) ? validatorReturnValue : fallbackErrorMessage;
}
return null;
},
_getStringValue: function Sys_Mvc_FieldContext$_getStringValue() {
/// <returns type="String"></returns>
var elements = this.elements;
return (elements.length > 0) ? elements[0].value : null;
},
_markValidationFired: function Sys_Mvc_FieldContext$_markValidationFired() {
var elements = this.elements;
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
element[Sys.Mvc.FieldContext._hasValidationFiredTag] = true;
}
},
_onErrorCountChanged: function Sys_Mvc_FieldContext$_onErrorCountChanged() {
if (!this._errors.length) {
this._displaySuccess();
}
else {
this._displayError();
}
},
validate: function Sys_Mvc_FieldContext$validate(eventName) {
/// <param name="eventName" type="String">
/// </param>
/// <returns type="Array" elementType="String"></returns>
var validations = this.validations;
var errors = [];
var value = this._getStringValue();
for (var i = 0; i < validations.length; i++) {
var validation = validations[i];
var context = Sys.Mvc.$create_ValidationContext();
context.eventName = eventName;
context.fieldContext = this;
context.validation = validation;
var retVal = validation.validator(value, context);
var errorMessage = this._getErrorString(retVal, validation.fieldErrorMessage);
if (!Sys.Mvc._validationUtil.stringIsNullOrEmpty(errorMessage)) {
Array.add(errors, errorMessage);
}
}
this._markValidationFired();
this.clearErrors();
this.addErrors(errors);
return errors;
}
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.RangeValidator
Sys.Mvc.RangeValidator = function Sys_Mvc_RangeValidator(minimum, maximum) {
/// <param name="minimum" type="Number">
/// </param>
/// <param name="maximum" type="Number">
/// </param>
/// <field name="_minimum" type="Number">
/// </field>
/// <field name="_maximum" type="Number">
/// </field>
this._minimum = minimum;
this._maximum = maximum;
}
Sys.Mvc.RangeValidator.create = function Sys_Mvc_RangeValidator$create(rule) {
/// <param name="rule" type="Sys.Mvc.JsonValidationRule">
/// </param>
/// <returns type="Sys.Mvc.Validator"></returns>
var min = rule.ValidationParameters['min'];
var max = rule.ValidationParameters['max'];
return Function.createDelegate(new Sys.Mvc.RangeValidator(min, max), new Sys.Mvc.RangeValidator(min, max).validate);
}
Sys.Mvc.RangeValidator.prototype = {
_minimum: null,
_maximum: null,
validate: function Sys_Mvc_RangeValidator$validate(value, context) {
/// <param name="value" type="String">
/// </param>
/// <param name="context" type="Sys.Mvc.ValidationContext">
/// </param>
/// <returns type="Object"></returns>
if (Sys.Mvc._validationUtil.stringIsNullOrEmpty(value)) {
return true;
}
var n = Number.parseLocale(value);
return (!isNaN(n) && this._minimum <= n && n <= this._maximum);
}
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.RegularExpressionValidator
Sys.Mvc.RegularExpressionValidator = function Sys_Mvc_RegularExpressionValidator(pattern) {
/// <param name="pattern" type="String">
/// </param>
/// <field name="_pattern" type="String">
/// </field>
this._pattern = pattern;
}
Sys.Mvc.RegularExpressionValidator.create = function Sys_Mvc_RegularExpressionValidator$create(rule) {
/// <param name="rule" type="Sys.Mvc.JsonValidationRule">
/// </param>
/// <returns type="Sys.Mvc.Validator"></returns>
var pattern = rule.ValidationParameters['pattern'];
return Function.createDelegate(new Sys.Mvc.RegularExpressionValidator(pattern), new Sys.Mvc.RegularExpressionValidator(pattern).validate);
}
Sys.Mvc.RegularExpressionValidator.prototype = {
_pattern: null,
validate: function Sys_Mvc_RegularExpressionValidator$validate(value, context) {
/// <param name="value" type="String">
/// </param>
/// <param name="context" type="Sys.Mvc.ValidationContext">
/// </param>
/// <returns type="Object"></returns>
if (Sys.Mvc._validationUtil.stringIsNullOrEmpty(value)) {
return true;
}
var regExp = new RegExp(this._pattern);
var matches = regExp.exec(value);
return (!Sys.Mvc._validationUtil.arrayIsNullOrEmpty(matches) && matches[0].length === value.length);
}
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.RequiredValidator
Sys.Mvc.RequiredValidator = function Sys_Mvc_RequiredValidator() {
}
Sys.Mvc.RequiredValidator.create = function Sys_Mvc_RequiredValidator$create(rule) {
/// <param name="rule" type="Sys.Mvc.JsonValidationRule">
/// </param>
/// <returns type="Sys.Mvc.Validator"></returns>
return Function.createDelegate(new Sys.Mvc.RequiredValidator(), new Sys.Mvc.RequiredValidator().validate);
}
Sys.Mvc.RequiredValidator._isRadioInputElement = function Sys_Mvc_RequiredValidator$_isRadioInputElement(element) {
/// <param name="element" type="Object" domElement="true">
/// </param>
/// <returns type="Boolean"></returns>
if (element.tagName.toUpperCase() === 'INPUT') {
var inputType = (element.type).toUpperCase();
if (inputType === 'RADIO') {
return true;
}
}
return false;
}
Sys.Mvc.RequiredValidator._isSelectInputElement = function Sys_Mvc_RequiredValidator$_isSelectInputElement(element) {
/// <param name="element" type="Object" domElement="true">
/// </param>
/// <returns type="Boolean"></returns>
if (element.tagName.toUpperCase() === 'SELECT') {
return true;
}
return false;
}
Sys.Mvc.RequiredValidator._isTextualInputElement = function Sys_Mvc_RequiredValidator$_isTextualInputElement(element) {
/// <param name="element" type="Object" domElement="true">
/// </param>
/// <returns type="Boolean"></returns>
if (element.tagName.toUpperCase() === 'INPUT') {
var inputType = (element.type).toUpperCase();
switch (inputType) {
case 'TEXT':
case 'PASSWORD':
case 'FILE':
return true;
}
}
if (element.tagName.toUpperCase() === 'TEXTAREA') {
return true;
}
return false;
}
Sys.Mvc.RequiredValidator._validateRadioInput = function Sys_Mvc_RequiredValidator$_validateRadioInput(elements) {
/// <param name="elements" type="Array" elementType="Object" elementDomElement="true">
/// </param>
/// <returns type="Object"></returns>
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (element.checked) {
return true;
}
}
return false;
}
Sys.Mvc.RequiredValidator._validateSelectInput = function Sys_Mvc_RequiredValidator$_validateSelectInput(optionElements) {
/// <param name="optionElements" type="DOMElementCollection">
/// </param>
/// <returns type="Object"></returns>
for (var i = 0; i < optionElements.length; i++) {
var element = optionElements[i];
if (element.selected) {
if (!Sys.Mvc._validationUtil.stringIsNullOrEmpty(element.value)) {
return true;
}
}
}
return false;
}
Sys.Mvc.RequiredValidator._validateTextualInput = function Sys_Mvc_RequiredValidator$_validateTextualInput(element) {
/// <param name="element" type="Object" domElement="true">
/// </param>
/// <returns type="Object"></returns>
return (!Sys.Mvc._validationUtil.stringIsNullOrEmpty(element.value));
}
Sys.Mvc.RequiredValidator.prototype = {
validate: function Sys_Mvc_RequiredValidator$validate(value, context) {
/// <param name="value" type="String">
/// </param>
/// <param name="context" type="Sys.Mvc.ValidationContext">
/// </param>
/// <returns type="Object"></returns>
var elements = context.fieldContext.elements;
if (!elements.length) {
return true;
}
var sampleElement = elements[0];
if (Sys.Mvc.RequiredValidator._isTextualInputElement(sampleElement)) {
return Sys.Mvc.RequiredValidator._validateTextualInput(sampleElement);
}
if (Sys.Mvc.RequiredValidator._isRadioInputElement(sampleElement)) {
return Sys.Mvc.RequiredValidator._validateRadioInput(elements);
}
if (Sys.Mvc.RequiredValidator._isSelectInputElement(sampleElement)) {
return Sys.Mvc.RequiredValidator._validateSelectInput((sampleElement).options);
}
return true;
}
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.StringLengthValidator
Sys.Mvc.StringLengthValidator = function Sys_Mvc_StringLengthValidator(minLength, maxLength) {
/// <param name="minLength" type="Number" integer="true">
/// </param>
/// <param name="maxLength" type="Number" integer="true">
/// </param>
/// <field name="_maxLength" type="Number" integer="true">
/// </field>
/// <field name="_minLength" type="Number" integer="true">
/// </field>
this._minLength = minLength;
this._maxLength = maxLength;
}
Sys.Mvc.StringLengthValidator.create = function Sys_Mvc_StringLengthValidator$create(rule) {
/// <param name="rule" type="Sys.Mvc.JsonValidationRule">
/// </param>
/// <returns type="Sys.Mvc.Validator"></returns>
var minLength = (rule.ValidationParameters['min'] || 0);
var maxLength = (rule.ValidationParameters['max'] || Number.MAX_VALUE);
return Function.createDelegate(new Sys.Mvc.StringLengthValidator(minLength, maxLength), new Sys.Mvc.StringLengthValidator(minLength, maxLength).validate);
}
Sys.Mvc.StringLengthValidator.prototype = {
_maxLength: 0,
_minLength: 0,
validate: function Sys_Mvc_StringLengthValidator$validate(value, context) {
/// <param name="value" type="String">
/// </param>
/// <param name="context" type="Sys.Mvc.ValidationContext">
/// </param>
/// <returns type="Object"></returns>
if (Sys.Mvc._validationUtil.stringIsNullOrEmpty(value)) {
return true;
}
return (this._minLength <= value.length && value.length <= this._maxLength);
}
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc._validationUtil
Sys.Mvc._validationUtil = function Sys_Mvc__validationUtil() {
}
Sys.Mvc._validationUtil.arrayIsNullOrEmpty = function Sys_Mvc__validationUtil$arrayIsNullOrEmpty(array) {
/// <param name="array" type="Array" elementType="Object">
/// </param>
/// <returns type="Boolean"></returns>
return (!array || !array.length);
}
Sys.Mvc._validationUtil.stringIsNullOrEmpty = function Sys_Mvc__validationUtil$stringIsNullOrEmpty(value) {
/// <param name="value" type="String">
/// </param>
/// <returns type="Boolean"></returns>
return (!value || !value.length);
}
Sys.Mvc._validationUtil.elementSupportsEvent = function Sys_Mvc__validationUtil$elementSupportsEvent(element, eventAttributeName) {
/// <param name="element" type="Object" domElement="true">
/// </param>
/// <param name="eventAttributeName" type="String">
/// </param>
/// <returns type="Boolean"></returns>
return (eventAttributeName in element);
}
Sys.Mvc._validationUtil.removeAllChildren = function Sys_Mvc__validationUtil$removeAllChildren(element) {
/// <param name="element" type="Object" domElement="true">
/// </param>
while (element.firstChild) {
element.removeChild(element.firstChild);
}
}
Sys.Mvc._validationUtil.setInnerText = function Sys_Mvc__validationUtil$setInnerText(element, innerText) {
/// <param name="element" type="Object" domElement="true">
/// </param>
/// <param name="innerText" type="String">
/// </param>
var textNode = document.createTextNode(innerText);
Sys.Mvc._validationUtil.removeAllChildren(element);
element.appendChild(textNode);
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.ValidatorRegistry
Sys.Mvc.ValidatorRegistry = function Sys_Mvc_ValidatorRegistry() {
/// <field name="validators" type="Object" static="true">
/// </field>
}
Sys.Mvc.ValidatorRegistry.getValidator = function Sys_Mvc_ValidatorRegistry$getValidator(rule) {
/// <param name="rule" type="Sys.Mvc.JsonValidationRule">
/// </param>
/// <returns type="Sys.Mvc.Validator"></returns>
var creator = Sys.Mvc.ValidatorRegistry.validators[rule.ValidationType];
return (creator) ? creator(rule) : null;
}
Sys.Mvc.ValidatorRegistry._getDefaultValidators = function Sys_Mvc_ValidatorRegistry$_getDefaultValidators() {
/// <returns type="Object"></returns>
return { required: Function.createDelegate(null, Sys.Mvc.RequiredValidator.create), length: Function.createDelegate(null, Sys.Mvc.StringLengthValidator.create), regex: Function.createDelegate(null, Sys.Mvc.RegularExpressionValidator.create), range: Function.createDelegate(null, Sys.Mvc.RangeValidator.create), number: Function.createDelegate(null, Sys.Mvc.NumberValidator.create) };
}
Sys.Mvc.NumberValidator.registerClass('Sys.Mvc.NumberValidator');
Sys.Mvc.FormContext.registerClass('Sys.Mvc.FormContext');
Sys.Mvc.FieldContext.registerClass('Sys.Mvc.FieldContext');
Sys.Mvc.RangeValidator.registerClass('Sys.Mvc.RangeValidator');
Sys.Mvc.RegularExpressionValidator.registerClass('Sys.Mvc.RegularExpressionValidator');
Sys.Mvc.RequiredValidator.registerClass('Sys.Mvc.RequiredValidator');
Sys.Mvc.StringLengthValidator.registerClass('Sys.Mvc.StringLengthValidator');
Sys.Mvc._validationUtil.registerClass('Sys.Mvc._validationUtil');
Sys.Mvc.ValidatorRegistry.registerClass('Sys.Mvc.ValidatorRegistry');
Sys.Mvc.FormContext._validationSummaryErrorCss = 'validation-summary-errors';
Sys.Mvc.FormContext._validationSummaryValidCss = 'validation-summary-valid';
Sys.Mvc.FormContext._formValidationTag = '__MVC_FormValidation';
Sys.Mvc.FieldContext._hasTextChangedTag = '__MVC_HasTextChanged';
Sys.Mvc.FieldContext._hasValidationFiredTag = '__MVC_HasValidationFired';
Sys.Mvc.FieldContext._inputElementErrorCss = 'input-validation-error';
Sys.Mvc.FieldContext._inputElementValidCss = 'input-validation-valid';
Sys.Mvc.FieldContext._validationMessageErrorCss = 'field-validation-error';
Sys.Mvc.FieldContext._validationMessageValidCss = 'field-validation-valid';
Sys.Mvc.ValidatorRegistry.validators = Sys.Mvc.ValidatorRegistry._getDefaultValidators();
// ---- Do not remove this footer ----
// Generated using Script# v0.5.0.0 (http://projects.nikhilk.net)
// -----------------------------------
// register validation
Sys.Application.add_load(function() {
Sys.Application.remove_load(arguments.callee);
Sys.Mvc.FormContext._Application_Load();
});

55
src/Test/Test/Scripts/MicrosoftMvcValidation.js

@ -0,0 +1,55 @@
//----------------------------------------------------------
// Copyright (C) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------
// MicrosoftMvcValidation.js
Type.registerNamespace('Sys.Mvc');Sys.Mvc.$create_Validation=function(){return {};}
Sys.Mvc.$create_JsonValidationField=function(){return {};}
Sys.Mvc.$create_JsonValidationOptions=function(){return {};}
Sys.Mvc.$create_JsonValidationRule=function(){return {};}
Sys.Mvc.$create_ValidationContext=function(){return {};}
Sys.Mvc.NumberValidator=function(){}
Sys.Mvc.NumberValidator.create=function(rule){return Function.createDelegate(new Sys.Mvc.NumberValidator(),new Sys.Mvc.NumberValidator().validate);}
Sys.Mvc.NumberValidator.prototype={validate:function(value,context){if(Sys.Mvc._ValidationUtil.$1(value)){return true;}var $0=Number.parseLocale(value);return (!isNaN($0));}}
Sys.Mvc.FormContext=function(formElement,validationSummaryElement){this.$5=[];this.fields=new Array(0);this.$9=formElement;this.$7=validationSummaryElement;formElement['__MVC_FormValidation'] = this;if(validationSummaryElement){var $0=validationSummaryElement.getElementsByTagName('ul');if($0.length>0){this.$8=$0[0];}}this.$3=Function.createDelegate(this,this.$D);this.$4=Function.createDelegate(this,this.$E);}
Sys.Mvc.FormContext._Application_Load=function(){var $0=window.mvcClientValidationMetadata;if($0){while($0.length>0){var $1=$0.pop();Sys.Mvc.FormContext.$12($1);}}}
Sys.Mvc.FormContext.$F=function($p0,$p1){var $0=[];var $1=document.getElementsByName($p1);for(var $2=0;$2<$1.length;$2++){var $3=$1[$2];if(Sys.Mvc.FormContext.$10($p0,$3)){Array.add($0,$3);}}return $0;}
Sys.Mvc.FormContext.getValidationForForm=function(formElement){return formElement['__MVC_FormValidation'];}
Sys.Mvc.FormContext.$10=function($p0,$p1){while($p1){if($p0===$p1){return true;}$p1=$p1.parentNode;}return false;}
Sys.Mvc.FormContext.$12=function($p0){var $0=$get($p0.FormId);var $1=(!Sys.Mvc._ValidationUtil.$1($p0.ValidationSummaryId))?$get($p0.ValidationSummaryId):null;var $2=new Sys.Mvc.FormContext($0,$1);$2.enableDynamicValidation();$2.replaceValidationSummary=$p0.ReplaceValidationSummary;for(var $4=0;$4<$p0.Fields.length;$4++){var $5=$p0.Fields[$4];var $6=Sys.Mvc.FormContext.$F($0,$5.FieldName);var $7=(!Sys.Mvc._ValidationUtil.$1($5.ValidationMessageId))?$get($5.ValidationMessageId):null;var $8=new Sys.Mvc.FieldContext($2);Array.addRange($8.elements,$6);$8.validationMessageElement=$7;$8.replaceValidationMessageContents=$5.ReplaceValidationMessageContents;for(var $9=0;$9<$5.ValidationRules.length;$9++){var $A=$5.ValidationRules[$9];var $B=Sys.Mvc.ValidatorRegistry.getValidator($A);if($B){var $C=Sys.Mvc.$create_Validation();$C.fieldErrorMessage=$A.ErrorMessage;$C.validator=$B;Array.add($8.validations,$C);}}$8.enableDynamicValidation();Array.add($2.fields,$8);}var $3=$0.validationCallbacks;if(!$3){$3=[];$0.validationCallbacks = $3;}$3.push(Function.createDelegate(null,function(){
return Sys.Mvc._ValidationUtil.$0($2.validate('submit'));}));return $2;}
Sys.Mvc.FormContext.prototype={$3:null,$4:null,$6:null,$7:null,$8:null,$9:null,replaceValidationSummary:false,addError:function(message){this.addErrors([message]);},addErrors:function(messages){if(!Sys.Mvc._ValidationUtil.$0(messages)){Array.addRange(this.$5,messages);this.$11();}},clearErrors:function(){Array.clear(this.$5);this.$11();},$A:function(){if(this.$7){if(this.$8){Sys.Mvc._ValidationUtil.$3(this.$8);for(var $0=0;$0<this.$5.length;$0++){var $1=document.createElement('li');Sys.Mvc._ValidationUtil.$4($1,this.$5[$0]);this.$8.appendChild($1);}}Sys.UI.DomElement.removeCssClass(this.$7,'validation-summary-valid');Sys.UI.DomElement.addCssClass(this.$7,'validation-summary-errors');}},$B:function(){var $0=this.$7;if($0){var $1=this.$8;if($1){$1.innerHTML='';}Sys.UI.DomElement.removeCssClass($0,'validation-summary-errors');Sys.UI.DomElement.addCssClass($0,'validation-summary-valid');}},enableDynamicValidation:function(){Sys.UI.DomEvent.addHandler(this.$9,'click',this.$3);Sys.UI.DomEvent.addHandler(this.$9,'submit',this.$4);},$C:function($p0){if($p0.disabled){return null;}var $0=$p0.tagName.toUpperCase();var $1=$p0;if($0==='INPUT'){var $2=$1.type;if($2==='submit'||$2==='image'){return $1;}}else if(($0==='BUTTON')&&($1.type==='submit')){return $1;}return null;},$D:function($p0){this.$6=this.$C($p0.target);},$E:function($p0){var $0=$p0.target;var $1=this.$6;if($1&&$1.disableValidation){return;}var $2=this.validate('submit');if(!Sys.Mvc._ValidationUtil.$0($2)){$p0.preventDefault();}},$11:function(){if(!this.$5.length){this.$B();}else{this.$A();}},validate:function(eventName){var $0=this.fields;var $1=[];for(var $2=0;$2<$0.length;$2++){var $3=$0[$2];if(!$3.elements[0].disabled){var $4=$3.validate(eventName);if($4){Array.addRange($1,$4);}}}if(this.replaceValidationSummary){this.clearErrors();this.addErrors($1);}return $1;}}
Sys.Mvc.FieldContext=function(formContext){this.$A=[];this.elements=new Array(0);this.validations=new Array(0);this.formContext=formContext;this.$6=Function.createDelegate(this,this.$D);this.$7=Function.createDelegate(this,this.$E);this.$8=Function.createDelegate(this,this.$F);this.$9=Function.createDelegate(this,this.$10);}
Sys.Mvc.FieldContext.prototype={$6:null,$7:null,$8:null,$9:null,defaultErrorMessage:null,formContext:null,replaceValidationMessageContents:false,validationMessageElement:null,addError:function(message){this.addErrors([message]);},addErrors:function(messages){if(!Sys.Mvc._ValidationUtil.$0(messages)){Array.addRange(this.$A,messages);this.$14();}},clearErrors:function(){Array.clear(this.$A);this.$14();},$B:function(){var $0=this.validationMessageElement;if($0){if(this.replaceValidationMessageContents){Sys.Mvc._ValidationUtil.$4($0,this.$A[0]);}Sys.UI.DomElement.removeCssClass($0,'field-validation-valid');Sys.UI.DomElement.addCssClass($0,'field-validation-error');}var $1=this.elements;for(var $2=0;$2<$1.length;$2++){var $3=$1[$2];Sys.UI.DomElement.removeCssClass($3,'input-validation-valid');Sys.UI.DomElement.addCssClass($3,'input-validation-error');}},$C:function(){var $0=this.validationMessageElement;if($0){if(this.replaceValidationMessageContents){Sys.Mvc._ValidationUtil.$4($0,'');}Sys.UI.DomElement.removeCssClass($0,'field-validation-error');Sys.UI.DomElement.addCssClass($0,'field-validation-valid');}var $1=this.elements;for(var $2=0;$2<$1.length;$2++){var $3=$1[$2];Sys.UI.DomElement.removeCssClass($3,'input-validation-error');Sys.UI.DomElement.addCssClass($3,'input-validation-valid');}},$D:function($p0){if($p0.target['__MVC_HasTextChanged']||$p0.target['__MVC_HasValidationFired']){this.validate('blur');}},$E:function($p0){$p0.target['__MVC_HasTextChanged'] = true;},$F:function($p0){$p0.target['__MVC_HasTextChanged'] = true;if($p0.target['__MVC_HasValidationFired']){this.validate('input');}},$10:function($p0){if($p0.rawEvent.propertyName==='value'){$p0.target['__MVC_HasTextChanged'] = true;if($p0.target['__MVC_HasValidationFired']){this.validate('input');}}},enableDynamicValidation:function(){var $0=this.elements;for(var $1=0;$1<$0.length;$1++){var $2=$0[$1];if(Sys.Mvc._ValidationUtil.$2($2,'onpropertychange')){var $3=document.documentMode;if($3&&$3>=8){Sys.UI.DomEvent.addHandler($2,'propertychange',this.$9);}}else{Sys.UI.DomEvent.addHandler($2,'input',this.$8);}Sys.UI.DomEvent.addHandler($2,'change',this.$7);Sys.UI.DomEvent.addHandler($2,'blur',this.$6);}},$11:function($p0,$p1){var $0=$p1||this.defaultErrorMessage;if(Boolean.isInstanceOfType($p0)){return ($p0)?null:$0;}if(String.isInstanceOfType($p0)){return (($p0).length)?$p0:$0;}return null;},$12:function(){var $0=this.elements;return ($0.length>0)?$0[0].value:null;},$13:function(){var $0=this.elements;for(var $1=0;$1<$0.length;$1++){var $2=$0[$1];$2['__MVC_HasValidationFired'] = true;}},$14:function(){if(!this.$A.length){this.$C();}else{this.$B();}},validate:function(eventName){var $0=this.validations;var $1=[];var $2=this.$12();for(var $3=0;$3<$0.length;$3++){var $4=$0[$3];var $5=Sys.Mvc.$create_ValidationContext();$5.eventName=eventName;$5.fieldContext=this;$5.validation=$4;var $6=$4.validator($2,$5);var $7=this.$11($6,$4.fieldErrorMessage);if(!Sys.Mvc._ValidationUtil.$1($7)){Array.add($1,$7);}}this.$13();this.clearErrors();this.addErrors($1);return $1;}}
Sys.Mvc.RangeValidator=function(minimum,maximum){this.$0=minimum;this.$1=maximum;}
Sys.Mvc.RangeValidator.create=function(rule){var $0=rule.ValidationParameters['min'];var $1=rule.ValidationParameters['max'];return Function.createDelegate(new Sys.Mvc.RangeValidator($0,$1),new Sys.Mvc.RangeValidator($0,$1).validate);}
Sys.Mvc.RangeValidator.prototype={$0:null,$1:null,validate:function(value,context){if(Sys.Mvc._ValidationUtil.$1(value)){return true;}var $0=Number.parseLocale(value);return (!isNaN($0)&&this.$0<=$0&&$0<=this.$1);}}
Sys.Mvc.RegularExpressionValidator=function(pattern){this.$0=pattern;}
Sys.Mvc.RegularExpressionValidator.create=function(rule){var $0=rule.ValidationParameters['pattern'];return Function.createDelegate(new Sys.Mvc.RegularExpressionValidator($0),new Sys.Mvc.RegularExpressionValidator($0).validate);}
Sys.Mvc.RegularExpressionValidator.prototype={$0:null,validate:function(value,context){if(Sys.Mvc._ValidationUtil.$1(value)){return true;}var $0=new RegExp(this.$0);var $1=$0.exec(value);return (!Sys.Mvc._ValidationUtil.$0($1)&&$1[0].length===value.length);}}
Sys.Mvc.RequiredValidator=function(){}
Sys.Mvc.RequiredValidator.create=function(rule){return Function.createDelegate(new Sys.Mvc.RequiredValidator(),new Sys.Mvc.RequiredValidator().validate);}
Sys.Mvc.RequiredValidator.$0=function($p0){if($p0.tagName.toUpperCase()==='INPUT'){var $0=($p0.type).toUpperCase();if($0==='RADIO'){return true;}}return false;}
Sys.Mvc.RequiredValidator.$1=function($p0){if($p0.tagName.toUpperCase()==='SELECT'){return true;}return false;}
Sys.Mvc.RequiredValidator.$2=function($p0){if($p0.tagName.toUpperCase()==='INPUT'){var $0=($p0.type).toUpperCase();switch($0){case 'TEXT':case 'PASSWORD':case 'FILE':return true;}}if($p0.tagName.toUpperCase()==='TEXTAREA'){return true;}return false;}
Sys.Mvc.RequiredValidator.$3=function($p0){for(var $0=0;$0<$p0.length;$0++){var $1=$p0[$0];if($1.checked){return true;}}return false;}
Sys.Mvc.RequiredValidator.$4=function($p0){for(var $0=0;$0<$p0.length;$0++){var $1=$p0[$0];if($1.selected){if(!Sys.Mvc._ValidationUtil.$1($1.value)){return true;}}}return false;}
Sys.Mvc.RequiredValidator.$5=function($p0){return (!Sys.Mvc._ValidationUtil.$1($p0.value));}
Sys.Mvc.RequiredValidator.prototype={validate:function(value,context){var $0=context.fieldContext.elements;if(!$0.length){return true;}var $1=$0[0];if(Sys.Mvc.RequiredValidator.$2($1)){return Sys.Mvc.RequiredValidator.$5($1);}if(Sys.Mvc.RequiredValidator.$0($1)){return Sys.Mvc.RequiredValidator.$3($0);}if(Sys.Mvc.RequiredValidator.$1($1)){return Sys.Mvc.RequiredValidator.$4(($1).options);}return true;}}
Sys.Mvc.StringLengthValidator=function(minLength,maxLength){this.$1=minLength;this.$0=maxLength;}
Sys.Mvc.StringLengthValidator.create=function(rule){var $0=(rule.ValidationParameters['min']||0);var $1=(rule.ValidationParameters['max']||Number.MAX_VALUE);return Function.createDelegate(new Sys.Mvc.StringLengthValidator($0,$1),new Sys.Mvc.StringLengthValidator($0,$1).validate);}
Sys.Mvc.StringLengthValidator.prototype={$0:0,$1:0,validate:function(value,context){if(Sys.Mvc._ValidationUtil.$1(value)){return true;}return (this.$1<=value.length&&value.length<=this.$0);}}
Sys.Mvc._ValidationUtil=function(){}
Sys.Mvc._ValidationUtil.$0=function($p0){return (!$p0||!$p0.length);}
Sys.Mvc._ValidationUtil.$1=function($p0){return (!$p0||!$p0.length);}
Sys.Mvc._ValidationUtil.$2=function($p0,$p1){return ($p1 in $p0);}
Sys.Mvc._ValidationUtil.$3=function($p0){while($p0.firstChild){$p0.removeChild($p0.firstChild);}}
Sys.Mvc._ValidationUtil.$4=function($p0,$p1){var $0=document.createTextNode($p1);Sys.Mvc._ValidationUtil.$3($p0);$p0.appendChild($0);}
Sys.Mvc.ValidatorRegistry=function(){}
Sys.Mvc.ValidatorRegistry.getValidator=function(rule){var $0=Sys.Mvc.ValidatorRegistry.validators[rule.ValidationType];return ($0)?$0(rule):null;}
Sys.Mvc.ValidatorRegistry.$0=function(){return {required:Function.createDelegate(null,Sys.Mvc.RequiredValidator.create),length:Function.createDelegate(null,Sys.Mvc.StringLengthValidator.create),regex:Function.createDelegate(null,Sys.Mvc.RegularExpressionValidator.create),range:Function.createDelegate(null,Sys.Mvc.RangeValidator.create),number:Function.createDelegate(null,Sys.Mvc.NumberValidator.create)};}
Sys.Mvc.NumberValidator.registerClass('Sys.Mvc.NumberValidator');Sys.Mvc.FormContext.registerClass('Sys.Mvc.FormContext');Sys.Mvc.FieldContext.registerClass('Sys.Mvc.FieldContext');Sys.Mvc.RangeValidator.registerClass('Sys.Mvc.RangeValidator');Sys.Mvc.RegularExpressionValidator.registerClass('Sys.Mvc.RegularExpressionValidator');Sys.Mvc.RequiredValidator.registerClass('Sys.Mvc.RequiredValidator');Sys.Mvc.StringLengthValidator.registerClass('Sys.Mvc.StringLengthValidator');Sys.Mvc._ValidationUtil.registerClass('Sys.Mvc._ValidationUtil');Sys.Mvc.ValidatorRegistry.registerClass('Sys.Mvc.ValidatorRegistry');Sys.Mvc.ValidatorRegistry.validators=Sys.Mvc.ValidatorRegistry.$0();
// ---- Do not remove this footer ----
// Generated using Script# v0.5.0.0 (http://projects.nikhilk.net)
// -----------------------------------
Sys.Application.add_load(function(){Sys.Application.remove_load(arguments.callee);Sys.Mvc.FormContext._Application_Load();});

1
src/Test/Test/Scripts/jquery-1.5.1-vsdoc.js.REMOVED.git-id

@ -0,0 +1 @@
8f56f29ea15515370d52a560396067bb28b52005

1
src/Test/Test/Scripts/jquery-1.5.1.js.REMOVED.git-id

@ -0,0 +1 @@
5948d8cc4932a48bc126343cf1d5ea2ac5f0b3c0

1
src/Test/Test/Scripts/jquery-1.5.1.min.js.REMOVED.git-id

@ -0,0 +1 @@
eec584bc589cf1ab1fe50781c1780f89c90aa31b

1
src/Test/Test/Scripts/jquery-ui-1.8.11.js.REMOVED.git-id

@ -0,0 +1 @@
79285779228262f9c021d1ee5a455c6f7f4c1113

1
src/Test/Test/Scripts/jquery-ui-1.8.11.min.js.REMOVED.git-id

@ -0,0 +1 @@
89ff61bcba0fbfbe359c0d3734942d260b702a4b

165
src/Test/Test/Scripts/jquery.unobtrusive-ajax.js

@ -0,0 +1,165 @@
/// <reference path="jquery-1.5.1.js" />
/*!
** Unobtrusive Ajax support library for jQuery
** Copyright (C) Microsoft Corporation. All rights reserved.
*/
/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */
/*global window: false, jQuery: false */
(function ($) {
var data_click = "unobtrusiveAjaxClick",
data_validation = "unobtrusiveValidation";
function getFunction(code, argNames) {
var fn = window, parts = (code || "").split(".");
while (fn && parts.length) {
fn = fn[parts.shift()];
}
if (typeof (fn) === "function") {
return fn;
}
argNames.push(code);
return Function.constructor.apply(null, argNames);
}
function isMethodProxySafe(method) {
return method === "GET" || method === "POST";
}
function asyncOnBeforeSend(xhr, method) {
if (!isMethodProxySafe(method)) {
xhr.setRequestHeader("X-HTTP-Method-Override", method);
}
}
function asyncOnSuccess(element, data, contentType) {
var mode;
if (contentType.indexOf("application/x-javascript") !== -1) { // jQuery already executes JavaScript for us
return;
}
mode = (element.getAttribute("data-ajax-mode") || "").toUpperCase();
$(element.getAttribute("data-ajax-update")).each(function (i, update) {
var top;
switch (mode) {
case "BEFORE":
top = update.firstChild;
$("<div />").html(data).contents().each(function () {
update.insertBefore(this, top);
});
break;
case "AFTER":
$("<div />").html(data).contents().each(function () {
update.appendChild(this);
});
break;
default:
$(update).html(data);
break;
}
});
}
function asyncRequest(element, options) {
var confirm, loading, method, duration;
confirm = element.getAttribute("data-ajax-confirm");
if (confirm && !window.confirm(confirm)) {
return;
}
loading = $(element.getAttribute("data-ajax-loading"));
duration = element.getAttribute("data-ajax-loading-duration") || 0;
$.extend(options, {
type: element.getAttribute("data-ajax-method") || undefined,
url: element.getAttribute("data-ajax-url") || undefined,
beforeSend: function (xhr) {
var result;
asyncOnBeforeSend(xhr, method);
result = getFunction(element.getAttribute("data-ajax-begin"), ["xhr"]).apply(this, arguments);
if (result !== false) {
loading.show(duration);
}
return result;
},
complete: function () {
loading.hide(duration);
getFunction(element.getAttribute("data-ajax-complete"), ["xhr", "status"]).apply(this, arguments);
},
success: function (data, status, xhr) {
asyncOnSuccess(element, data, xhr.getResponseHeader("Content-Type") || "text/html");
getFunction(element.getAttribute("data-ajax-success"), ["data", "status", "xhr"]).apply(this, arguments);
},
error: getFunction(element.getAttribute("data-ajax-failure"), ["xhr", "status", "error"])
});
options.data.push({ name: "X-Requested-With", value: "XMLHttpRequest" });
method = options.type.toUpperCase();
if (!isMethodProxySafe(method)) {
options.type = "POST";
options.data.push({ name: "X-HTTP-Method-Override", value: method });
}
$.ajax(options);
}
function validate(form) {
var validationInfo = $(form).data(data_validation);
return !validationInfo || !validationInfo.validate || validationInfo.validate();
}
$("a[data-ajax=true]").live("click", function (evt) {
evt.preventDefault();
asyncRequest(this, {
url: this.href,
type: "GET",
data: []
});
});
$("form[data-ajax=true] input[type=image]").live("click", function (evt) {
var name = evt.target.name,
$target = $(evt.target),
form = $target.parents("form")[0],
offset = $target.offset();
$(form).data(data_click, [
{ name: name + ".x", value: Math.round(evt.pageX - offset.left) },
{ name: name + ".y", value: Math.round(evt.pageY - offset.top) }
]);
setTimeout(function () {
$(form).removeData(data_click);
}, 0);
});
$("form[data-ajax=true] :submit").live("click", function (evt) {
var name = evt.target.name,
form = $(evt.target).parents("form")[0];
$(form).data(data_click, name ? [{ name: name, value: evt.target.value }] : []);
setTimeout(function () {
$(form).removeData(data_click);
}, 0);
});
$("form[data-ajax=true]").live("submit", function (evt) {
var clickInfo = $(this).data(data_click) || [];
evt.preventDefault();
if (!validate(this)) {
return;
}
asyncRequest(this, {
url: this.action,
type: this.method || "GET",
data: clickInfo.concat($(this).serializeArray())
});
});
}(jQuery));

5
src/Test/Test/Scripts/jquery.unobtrusive-ajax.min.js

@ -0,0 +1,5 @@
/*
** Unobtrusive Ajax support library for jQuery
** Copyright (C) Microsoft Corporation. All rights reserved.
*/
(function(a){var b="unobtrusiveAjaxClick",g="unobtrusiveValidation";function c(d,b){var a=window,c=(d||"").split(".");while(a&&c.length)a=a[c.shift()];if(typeof a==="function")return a;b.push(d);return Function.constructor.apply(null,b)}function d(a){return a==="GET"||a==="POST"}function f(b,a){!d(a)&&b.setRequestHeader("X-HTTP-Method-Override",a)}function h(c,b,e){var d;if(e.indexOf("application/x-javascript")!==-1)return;d=(c.getAttribute("data-ajax-mode")||"").toUpperCase();a(c.getAttribute("data-ajax-update")).each(function(f,c){var e;switch(d){case"BEFORE":e=c.firstChild;a("<div />").html(b).contents().each(function(){c.insertBefore(this,e)});break;case"AFTER":a("<div />").html(b).contents().each(function(){c.appendChild(this)});break;default:a(c).html(b)}})}function e(b,e){var j,k,g,i;j=b.getAttribute("data-ajax-confirm");if(j&&!window.confirm(j))return;k=a(b.getAttribute("data-ajax-loading"));i=b.getAttribute("data-ajax-loading-duration")||0;a.extend(e,{type:b.getAttribute("data-ajax-method")||undefined,url:b.getAttribute("data-ajax-url")||undefined,beforeSend:function(d){var a;f(d,g);a=c(b.getAttribute("data-ajax-begin"),["xhr"]).apply(this,arguments);a!==false&&k.show(i);return a},complete:function(){k.hide(i);c(b.getAttribute("data-ajax-complete"),["xhr","status"]).apply(this,arguments)},success:function(a,e,d){h(b,a,d.getResponseHeader("Content-Type")||"text/html");c(b.getAttribute("data-ajax-success"),["data","status","xhr"]).apply(this,arguments)},error:c(b.getAttribute("data-ajax-failure"),["xhr","status","error"])});e.data.push({name:"X-Requested-With",value:"XMLHttpRequest"});g=e.type.toUpperCase();if(!d(g)){e.type="POST";e.data.push({name:"X-HTTP-Method-Override",value:g})}a.ajax(e)}function i(c){var b=a(c).data(g);return!b||!b.validate||b.validate()}a("a[data-ajax=true]").live("click",function(a){a.preventDefault();e(this,{url:this.href,type:"GET",data:[]})});a("form[data-ajax=true] input[type=image]").live("click",function(c){var g=c.target.name,d=a(c.target),f=d.parents("form")[0],e=d.offset();a(f).data(b,[{name:g+".x",value:Math.round(c.pageX-e.left)},{name:g+".y",value:Math.round(c.pageY-e.top)}]);setTimeout(function(){a(f).removeData(b)},0)});a("form[data-ajax=true] :submit").live("click",function(c){var e=c.target.name,d=a(c.target).parents("form")[0];a(d).data(b,e?[{name:e,value:c.target.value}]:[]);setTimeout(function(){a(d).removeData(b)},0)});a("form[data-ajax=true]").live("submit",function(d){var c=a(this).data(b)||[];d.preventDefault();if(!i(this))return;e(this,{url:this.action,type:this.method||"GET",data:c.concat(a(this).serializeArray())})})})(jQuery);

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save