@ -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 |
|||
@ -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 |
|||
@ -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. |
|||
|
|||
|
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
|
|||
|
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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> |
|||
@ -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> |
|||
@ -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")] |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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> |
|||
@ -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 |
|||
@ -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> |
|||
@ -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> |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
@ -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); } |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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 } |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
|
|||
@ -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")] |
|||
@ -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 |
|||
@ -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; |
|||
} |
|||
|
After Width: | Height: | Size: 180 B |
|
After Width: | Height: | Size: 178 B |
|
After Width: | Height: | Size: 120 B |
|
After Width: | Height: | Size: 105 B |
|
After Width: | Height: | Size: 111 B |
|
After Width: | Height: | Size: 110 B |
|
After Width: | Height: | Size: 119 B |
|
After Width: | Height: | Size: 101 B |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
@ -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; } |
|||
@ -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"; |
|||
@ -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; |
|||
} |
|||
@ -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"); |
|||
@ -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 */ |
|||
@ -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%; } |
|||
@ -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*/ |
|||
} |
|||
@ -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; } |
|||
@ -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%; } |
|||
@ -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;} |
|||
@ -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; } |
|||
@ -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; } |
|||
@ -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; } |
|||
@ -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}*/; } |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
<%@ Application Codebehind="Global.asax.cs" Inherits="Test.MvcApplication" Language="C#" %> |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 50 KiB |
@ -0,0 +1 @@ |
|||
4d040d9aa3519b3d2303419d1f03eebebf88e956 |
|||
@ -0,0 +1 @@ |
|||
757c2a628dd03b1cbe4b3ef07c153897a702b57a |
|||
@ -0,0 +1 @@ |
|||
acbb9eee53e909814665053e0713335921c7d205 |
|||
@ -0,0 +1 @@ |
|||
0b88c91336ff8073f34d21ccd683a01f0e0995da |
|||
@ -0,0 +1 @@ |
|||
a587c9656db0f4773fbada4807b9377df2216d77 |
|||
@ -0,0 +1 @@ |
|||
fa4fd4110616804c956a8fe4c388700ec4408eef |
|||
@ -0,0 +1 @@ |
|||
78704a099bad91c76ecb96417137464b0fa96b28 |
|||
@ -0,0 +1 @@ |
|||
494be09b8b43cfce7550ba6d54ff3cf799e4c315 |
|||
@ -0,0 +1 @@ |
|||
33b6912af301bf216ee81d82b2c3ce6c49e03021 |
|||
@ -0,0 +1 @@ |
|||
030ab8a685bebb796c24cc710edd9e69859164f6 |
|||
@ -0,0 +1 @@ |
|||
54c51eb6a86f31a42433b8167470fb18dad32c7d |
|||
@ -0,0 +1 @@ |
|||
30b51f2b174d67995deb595343e3cef4483d64e4 |
|||
@ -0,0 +1 @@ |
|||
f945a806925fa3a763bfd4ec421134c217c46494 |
|||
@ -0,0 +1 @@ |
|||
c274f9d59c7f206c1cdf0cf97fd0cc13fc443191 |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -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")] |
|||
@ -0,0 +1 @@ |
|||
a5f7942ef2b6b06e3c1aac2110fe7e5a1d88bf51 |
|||
@ -0,0 +1 @@ |
|||
52e6626a072ad18c35926f81b94995a0c5eeee31 |
|||
@ -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)
|
|||
// -----------------------------------
|
|||
@ -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)
|
|||
// -----------------------------------
|
|||
@ -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(); |
|||
}); |
|||
@ -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();}); |
|||
@ -0,0 +1 @@ |
|||
8f56f29ea15515370d52a560396067bb28b52005 |
|||
@ -0,0 +1 @@ |
|||
5948d8cc4932a48bc126343cf1d5ea2ac5f0b3c0 |
|||
@ -0,0 +1 @@ |
|||
eec584bc589cf1ab1fe50781c1780f89c90aa31b |
|||
@ -0,0 +1 @@ |
|||
79285779228262f9c021d1ee5a455c6f7f4c1113 |
|||
@ -0,0 +1 @@ |
|||
89ff61bcba0fbfbe359c0d3734942d260b702a4b |
|||
@ -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)); |
|||
@ -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); |
|||