598 lines
23 KiB
C#
598 lines
23 KiB
C#
// Material sourced from the bluePortal project (http://blueportal.codeplex.com).
|
|
// Licensed under the Microsoft Public License (available at http://www.opensource.org/licenses/ms-pl.html).
|
|
|
|
using System;
|
|
using System.Data;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Configuration;
|
|
using System.Reflection;
|
|
using System.Web;
|
|
using System.Web.Configuration;
|
|
using System.Web.Security;
|
|
using System.Web.UI;
|
|
using System.Web.UI.Adapters;
|
|
using System.Web.UI.WebControls;
|
|
using System.Web.UI.WebControls.Adapters;
|
|
using System.Web.UI.WebControls.WebParts;
|
|
using System.Web.UI.HtmlControls;
|
|
|
|
namespace CSSFriendly
|
|
{
|
|
public class WebControlAdapterExtender
|
|
{
|
|
private WebControl _adaptedControl = null;
|
|
public WebControl AdaptedControl
|
|
{
|
|
get
|
|
{
|
|
System.Diagnostics.Debug.Assert(_adaptedControl != null, "CSS Friendly adapters internal error", "No control has been defined for the adapter extender");
|
|
return _adaptedControl;
|
|
}
|
|
}
|
|
|
|
public bool AdapterEnabled
|
|
{
|
|
get
|
|
{
|
|
bool bReturn = true; // normally the adapters are enabled
|
|
|
|
// Individual controls can use the expando property called AdapterEnabled
|
|
// as a way to turn the adapters off.
|
|
// <asp:TreeView runat="server" AdapterEnabled="false" />
|
|
if ((AdaptedControl != null) &&
|
|
(!String.IsNullOrEmpty(AdaptedControl.Attributes["AdapterEnabled"])) &&
|
|
(AdaptedControl.Attributes["AdapterEnabled"].IndexOf("false", StringComparison.OrdinalIgnoreCase) == 0))
|
|
{
|
|
bReturn = false;
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
}
|
|
|
|
private bool _disableAutoAccessKey = false; // used when dealing with things like read-only textboxes that should not have access keys
|
|
public bool AutoAccessKey
|
|
{
|
|
get
|
|
{
|
|
// Individual controls can use the expando property called AdapterEnabled
|
|
// as a way to turn on/off the heurisitic for automatically setting the AccessKey
|
|
// attribute in the rendered HTML. The default is shown below in the initialization
|
|
// of the bReturn variable.
|
|
// <asp:TreeView runat="server" AutoAccessKey="false" />
|
|
|
|
bool bReturn = true; // by default, the adapter will make access keys are available
|
|
if (_disableAutoAccessKey ||
|
|
((AdaptedControl != null) &&
|
|
(!String.IsNullOrEmpty(AdaptedControl.Attributes["AutoAccessKey"])) &&
|
|
(AdaptedControl.Attributes["AutoAccessKey"].IndexOf("false", StringComparison.OrdinalIgnoreCase) == 0)))
|
|
{
|
|
bReturn = false;
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
}
|
|
|
|
public WebControlAdapterExtender(WebControl adaptedControl)
|
|
{
|
|
_adaptedControl = adaptedControl;
|
|
}
|
|
|
|
public void RegisterScripts()
|
|
{
|
|
string folderPath = WebConfigurationManager.AppSettings.Get("CSSFriendly-JavaScript-Path");
|
|
if (String.IsNullOrEmpty(folderPath))
|
|
{
|
|
folderPath = "~/JavaScript";
|
|
}
|
|
string filePath = folderPath.EndsWith("/") ? folderPath + "AdapterUtils.js" : folderPath + "/AdapterUtils.js";
|
|
AdaptedControl.Page.ClientScript.RegisterClientScriptInclude(GetType(), GetType().ToString(), AdaptedControl.Page.ResolveUrl(filePath));
|
|
}
|
|
|
|
public string ResolveUrl(string url)
|
|
{
|
|
string urlToResolve = url;
|
|
int nPound = url.LastIndexOf("#");
|
|
int nSlash = url.LastIndexOf("/");
|
|
if ((nPound > -1) && (nSlash > -1) && ((nSlash + 1) == nPound))
|
|
{
|
|
// We have been given a somewhat strange URL. It has a foreward slash (/) immediately followed
|
|
// by a pound sign (#) like this xxx/#yyy. This sort of oddly shaped URL is what you get when
|
|
// you use named anchors instead of pages in the url attribute of a sitemapnode in an ASP.NET
|
|
// sitemap like this:
|
|
//
|
|
// <siteMapNode url="#Introduction" title="Introduction" description="Introduction" />
|
|
//
|
|
// The intend of the sitemap author is clearly to create a link to a named anchor in the page
|
|
// that looks like these:
|
|
//
|
|
// <a id="Introduction"></a> (XHTML 1.1 Strict compliant)
|
|
// <a name="Introduction"></a> (more old fashioned but quite common in many pages)
|
|
//
|
|
// However, the sitemap interpretter in ASP.NET doesn't understand url values that start with
|
|
// a pound. It prepends the current site's path in front of it before making it into a link
|
|
// (typically for a TreeView or Menu). We'll undo that problem, however, by converting this
|
|
// sort of oddly shaped URL back into what was intended: a simple reference to a named anchor
|
|
// that is expected to be within the current page.
|
|
|
|
urlToResolve = url.Substring(nPound);
|
|
}
|
|
else
|
|
{
|
|
urlToResolve = AdaptedControl.ResolveClientUrl(urlToResolve);
|
|
}
|
|
|
|
// And, just to be safe, we'll make sure there aren't any troublesome characters in whatever URL
|
|
// we have decided to use at this point.
|
|
string newUrl = AdaptedControl.Page.Server.HtmlEncode(urlToResolve);
|
|
|
|
return newUrl;
|
|
}
|
|
|
|
public void RaiseAdaptedEvent(string eventName, EventArgs e)
|
|
{
|
|
string attr = "OnAdapted" + eventName;
|
|
if ((AdaptedControl != null) && (!String.IsNullOrEmpty(AdaptedControl.Attributes[attr])))
|
|
{
|
|
string delegateName = AdaptedControl.Attributes[attr];
|
|
Control methodOwner = AdaptedControl.Parent;
|
|
MethodInfo method = methodOwner.GetType().GetMethod(delegateName);
|
|
if (method == null)
|
|
{
|
|
methodOwner = AdaptedControl.Page;
|
|
method = methodOwner.GetType().GetMethod(delegateName);
|
|
}
|
|
if (method != null)
|
|
{
|
|
object[] args = new object[2];
|
|
args[0] = AdaptedControl;
|
|
args[1] = e;
|
|
method.Invoke(methodOwner, args);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void RenderBeginTag(HtmlTextWriter writer, string cssClass)
|
|
{
|
|
if (!String.IsNullOrEmpty(AdaptedControl.Attributes["CssSelectorClass"]))
|
|
{
|
|
WriteBeginDiv(writer, AdaptedControl.Attributes["CssSelectorClass"]);
|
|
}
|
|
|
|
WriteBeginDiv(writer, cssClass);
|
|
}
|
|
|
|
public void RenderEndTag(HtmlTextWriter writer)
|
|
{
|
|
WriteEndDiv(writer);
|
|
|
|
if (!String.IsNullOrEmpty(AdaptedControl.Attributes["CssSelectorClass"]))
|
|
{
|
|
WriteEndDiv(writer);
|
|
}
|
|
}
|
|
|
|
static public void RemoveProblemChildren(Control ctrl, List<ControlRestorationInfo> stashedControls)
|
|
{
|
|
RemoveProblemTypes(ctrl.Controls, stashedControls);
|
|
}
|
|
|
|
static public void RemoveProblemTypes(ControlCollection coll, List<ControlRestorationInfo> stashedControls)
|
|
{
|
|
foreach (Control ctrl in coll)
|
|
{
|
|
if (typeof(RequiredFieldValidator).IsAssignableFrom(ctrl.GetType()) ||
|
|
typeof(CompareValidator).IsAssignableFrom(ctrl.GetType()) ||
|
|
typeof(RegularExpressionValidator).IsAssignableFrom(ctrl.GetType()) ||
|
|
typeof(ValidationSummary).IsAssignableFrom(ctrl.GetType()))
|
|
{
|
|
ControlRestorationInfo cri = new ControlRestorationInfo(ctrl, coll);
|
|
stashedControls.Add(cri);
|
|
coll.Remove(ctrl);
|
|
continue;
|
|
}
|
|
|
|
if (ctrl.HasControls())
|
|
{
|
|
RemoveProblemTypes(ctrl.Controls, stashedControls);
|
|
}
|
|
}
|
|
}
|
|
|
|
static public void RestoreProblemChildren(List<ControlRestorationInfo> stashedControls)
|
|
{
|
|
foreach (ControlRestorationInfo cri in stashedControls)
|
|
{
|
|
cri.Restore();
|
|
}
|
|
}
|
|
|
|
public string MakeChildId(string postfix)
|
|
{
|
|
return AdaptedControl.ClientID + "_" + postfix;
|
|
}
|
|
|
|
static public string MakeNameFromId(string id)
|
|
{
|
|
string name = "";
|
|
for (int i=0; i<id.Length; i++)
|
|
{
|
|
char thisChar = id[i];
|
|
char prevChar = ((i - 1) > -1) ? id[i - 1] : ' ';
|
|
char nextChar = ((i + 1) < id.Length) ? id[i + 1] : ' ';
|
|
if (thisChar == '_')
|
|
{
|
|
if (prevChar == '_')
|
|
{
|
|
name += "_";
|
|
}
|
|
else if (nextChar == '_')
|
|
{
|
|
name += "$_";
|
|
}
|
|
else
|
|
{
|
|
name += "$";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
name += thisChar;
|
|
}
|
|
}
|
|
return name;
|
|
}
|
|
|
|
static public string MakeIdWithButtonType(string id, ButtonType type)
|
|
{
|
|
string idWithType = id;
|
|
switch (type)
|
|
{
|
|
case ButtonType.Button:
|
|
idWithType += "Button";
|
|
break;
|
|
case ButtonType.Image:
|
|
idWithType += "ImageButton";
|
|
break;
|
|
case ButtonType.Link:
|
|
idWithType += "LinkButton";
|
|
break;
|
|
}
|
|
return idWithType;
|
|
}
|
|
|
|
public string MakeChildName(string postfix)
|
|
{
|
|
return MakeNameFromId(MakeChildId(postfix));
|
|
}
|
|
|
|
static public void WriteBeginDiv(HtmlTextWriter writer, string className)
|
|
{
|
|
writer.WriteLine();
|
|
writer.WriteBeginTag("div");
|
|
if (!String.IsNullOrEmpty(className))
|
|
{
|
|
writer.WriteAttribute("class", className);
|
|
}
|
|
writer.Write(HtmlTextWriter.TagRightChar);
|
|
writer.Indent++;
|
|
}
|
|
|
|
static public void WriteEndDiv(HtmlTextWriter writer)
|
|
{
|
|
writer.Indent--;
|
|
writer.WriteLine();
|
|
writer.WriteEndTag("div");
|
|
}
|
|
|
|
static public void WriteSpan(HtmlTextWriter writer, string className, string content)
|
|
{
|
|
if (!String.IsNullOrEmpty(content))
|
|
{
|
|
writer.WriteLine();
|
|
writer.WriteBeginTag("span");
|
|
if (!String.IsNullOrEmpty(className))
|
|
{
|
|
writer.WriteAttribute("class", className);
|
|
}
|
|
writer.Write(HtmlTextWriter.TagRightChar);
|
|
writer.Write(content);
|
|
writer.WriteEndTag("span");
|
|
}
|
|
}
|
|
|
|
static public void WriteImage(HtmlTextWriter writer, string url, string alt)
|
|
{
|
|
if (!String.IsNullOrEmpty(url))
|
|
{
|
|
writer.WriteLine();
|
|
writer.WriteBeginTag("img");
|
|
writer.WriteAttribute("src", url);
|
|
writer.WriteAttribute("alt", alt);
|
|
writer.Write(HtmlTextWriter.SelfClosingTagEnd);
|
|
}
|
|
}
|
|
|
|
static public void WriteLink(HtmlTextWriter writer, string className, string url, string title, string content)
|
|
{
|
|
if ((!String.IsNullOrEmpty(url)) && (!String.IsNullOrEmpty(content)))
|
|
{
|
|
writer.WriteLine();
|
|
writer.WriteBeginTag("a");
|
|
if (!String.IsNullOrEmpty(className))
|
|
{
|
|
writer.WriteAttribute("class", className);
|
|
}
|
|
writer.WriteAttribute("href", url);
|
|
writer.WriteAttribute("title", title);
|
|
writer.Write(HtmlTextWriter.TagRightChar);
|
|
writer.Write(content);
|
|
writer.WriteEndTag("a");
|
|
}
|
|
}
|
|
|
|
// Can't be static because it uses MakeChildId
|
|
public void WriteLabel(HtmlTextWriter writer, string className, string text, string forId)
|
|
{
|
|
if (!String.IsNullOrEmpty(text))
|
|
{
|
|
writer.WriteLine();
|
|
writer.WriteBeginTag("label");
|
|
writer.WriteAttribute("for", MakeChildId(forId));
|
|
if (!String.IsNullOrEmpty(className))
|
|
{
|
|
writer.WriteAttribute("class", className);
|
|
}
|
|
writer.Write(HtmlTextWriter.TagRightChar);
|
|
|
|
if (AutoAccessKey)
|
|
{
|
|
writer.WriteBeginTag("em");
|
|
writer.Write(HtmlTextWriter.TagRightChar);
|
|
writer.Write(text[0].ToString());
|
|
writer.WriteEndTag("em");
|
|
if (!String.IsNullOrEmpty(text))
|
|
{
|
|
writer.Write(text.Substring(1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
writer.Write(text);
|
|
}
|
|
|
|
writer.WriteEndTag("label");
|
|
}
|
|
}
|
|
|
|
// Can't be static because it uses MakeChildId
|
|
public void WriteTextBox(HtmlTextWriter writer, bool isPassword, string labelClassName, string labelText, string inputClassName, string id, string value)
|
|
{
|
|
WriteLabel(writer, labelClassName, labelText, id);
|
|
|
|
writer.WriteLine();
|
|
writer.WriteBeginTag("input");
|
|
writer.WriteAttribute("type", isPassword ? "password" : "text");
|
|
if (!String.IsNullOrEmpty(inputClassName))
|
|
{
|
|
writer.WriteAttribute("class", inputClassName);
|
|
}
|
|
writer.WriteAttribute("id", MakeChildId(id));
|
|
writer.WriteAttribute("name", MakeChildName(id));
|
|
writer.WriteAttribute("value", value);
|
|
if (AutoAccessKey && (!String.IsNullOrEmpty(labelText)))
|
|
{
|
|
writer.WriteAttribute("accesskey", labelText[0].ToString().ToLower());
|
|
}
|
|
writer.Write(HtmlTextWriter.SelfClosingTagEnd);
|
|
}
|
|
|
|
// Can't be static because it uses MakeChildId
|
|
public void WriteReadOnlyTextBox(HtmlTextWriter writer, string labelClassName, string labelText, string inputClassName, string value)
|
|
{
|
|
bool oldDisableAutoAccessKey = _disableAutoAccessKey;
|
|
_disableAutoAccessKey = true;
|
|
|
|
WriteLabel(writer, labelClassName, labelText, "");
|
|
|
|
writer.WriteLine();
|
|
writer.WriteBeginTag("input");
|
|
writer.WriteAttribute("readonly", "true");
|
|
if (!String.IsNullOrEmpty(inputClassName))
|
|
{
|
|
writer.WriteAttribute("class", inputClassName);
|
|
}
|
|
writer.WriteAttribute("value", value);
|
|
writer.Write(HtmlTextWriter.SelfClosingTagEnd);
|
|
|
|
_disableAutoAccessKey = oldDisableAutoAccessKey;
|
|
}
|
|
|
|
// Can't be static because it uses MakeChildId
|
|
public void WriteCheckBox(HtmlTextWriter writer, string labelClassName, string labelText, string inputClassName, string id, bool isChecked)
|
|
{
|
|
writer.WriteLine();
|
|
writer.WriteBeginTag("input");
|
|
writer.WriteAttribute("type", "checkbox");
|
|
if (!String.IsNullOrEmpty(inputClassName))
|
|
{
|
|
writer.WriteAttribute("class", inputClassName);
|
|
}
|
|
writer.WriteAttribute("id", MakeChildId(id));
|
|
writer.WriteAttribute("name", MakeChildName(id));
|
|
if (isChecked)
|
|
{
|
|
writer.WriteAttribute("checked", "checked");
|
|
}
|
|
if (AutoAccessKey && (!String.IsNullOrEmpty(labelText)))
|
|
{
|
|
writer.WriteAttribute("accesskey", labelText[0].ToString());
|
|
}
|
|
writer.Write(HtmlTextWriter.SelfClosingTagEnd);
|
|
|
|
WriteLabel(writer, labelClassName, labelText, id);
|
|
}
|
|
|
|
// Can't be static because it uses MakeChildId
|
|
public void WriteSubmit(HtmlTextWriter writer, ButtonType buttonType, string className, string id, string imageUrl, string javascript, string text)
|
|
{
|
|
writer.WriteLine();
|
|
|
|
string idWithType = id;
|
|
|
|
switch (buttonType)
|
|
{
|
|
case ButtonType.Button:
|
|
writer.WriteBeginTag("input");
|
|
writer.WriteAttribute("type", "submit");
|
|
writer.WriteAttribute("value", text);
|
|
idWithType += "Button";
|
|
break;
|
|
case ButtonType.Image:
|
|
writer.WriteBeginTag("input");
|
|
writer.WriteAttribute("type", "image");
|
|
writer.WriteAttribute("src", imageUrl);
|
|
idWithType += "ImageButton";
|
|
break;
|
|
case ButtonType.Link:
|
|
writer.WriteBeginTag("a");
|
|
idWithType += "LinkButton";
|
|
break;
|
|
}
|
|
|
|
if (!String.IsNullOrEmpty(className))
|
|
{
|
|
writer.WriteAttribute("class", className);
|
|
}
|
|
writer.WriteAttribute("id", MakeChildId(idWithType));
|
|
writer.WriteAttribute("name", MakeChildName(idWithType));
|
|
|
|
if (!String.IsNullOrEmpty(javascript))
|
|
{
|
|
string pureJS = javascript;
|
|
if (pureJS.StartsWith("javascript:"))
|
|
{
|
|
pureJS = pureJS.Substring("javascript:".Length);
|
|
}
|
|
switch (buttonType)
|
|
{
|
|
case ButtonType.Button:
|
|
writer.WriteAttribute("onclick", pureJS);
|
|
break;
|
|
case ButtonType.Image:
|
|
writer.WriteAttribute("onclick", pureJS);
|
|
break;
|
|
case ButtonType.Link:
|
|
writer.WriteAttribute("href", javascript);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (buttonType == ButtonType.Link)
|
|
{
|
|
writer.Write(HtmlTextWriter.TagRightChar);
|
|
writer.Write(text);
|
|
writer.WriteEndTag("a");
|
|
}
|
|
else
|
|
{
|
|
writer.Write(HtmlTextWriter.SelfClosingTagEnd);
|
|
}
|
|
}
|
|
|
|
static public void WriteRequiredFieldValidator(HtmlTextWriter writer, RequiredFieldValidator rfv, string className, string controlToValidate, string msg)
|
|
{
|
|
if (rfv != null)
|
|
{
|
|
rfv.CssClass = className;
|
|
rfv.ControlToValidate = controlToValidate;
|
|
rfv.ErrorMessage = msg;
|
|
rfv.RenderControl(writer);
|
|
}
|
|
}
|
|
|
|
static public void WriteRegularExpressionValidator(HtmlTextWriter writer, RegularExpressionValidator rev, string className, string controlToValidate, string msg, string expression)
|
|
{
|
|
if (rev != null)
|
|
{
|
|
rev.CssClass = className;
|
|
rev.ControlToValidate = controlToValidate;
|
|
rev.ErrorMessage = msg;
|
|
rev.ValidationExpression = expression;
|
|
rev.RenderControl(writer);
|
|
}
|
|
}
|
|
|
|
static public void WriteCompareValidator(HtmlTextWriter writer, CompareValidator cv, string className, string controlToValidate, string msg, string controlToCompare)
|
|
{
|
|
if (cv != null)
|
|
{
|
|
cv.CssClass = className;
|
|
cv.ControlToValidate = controlToValidate;
|
|
cv.ErrorMessage = msg;
|
|
cv.ControlToCompare = controlToCompare;
|
|
cv.RenderControl(writer);
|
|
}
|
|
}
|
|
|
|
static public void WriteTargetAttribute(HtmlTextWriter writer, string targetValue)
|
|
{
|
|
if ((writer != null) && (!String.IsNullOrEmpty(targetValue)))
|
|
{
|
|
// If the targetValue is _blank then we have an opportunity to use attributes other than "target"
|
|
// which allows us to be compliant at the XHTML 1.1 Strict level. Specifically, we can use a combination
|
|
// of "onclick" and "onkeypress" to achieve what we want to achieve when we used to render
|
|
// target='blank'.
|
|
//
|
|
// If the targetValue is other than _blank then we fall back to using the "target" attribute.
|
|
// This is a heuristic that can be refined over time.
|
|
if (targetValue.Equals("_blank", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
string js = "window.open(this.href, '_blank', ''); return false;";
|
|
writer.WriteAttribute("onclick", js);
|
|
writer.WriteAttribute("onkeypress", js);
|
|
}
|
|
else
|
|
{
|
|
writer.WriteAttribute("target", targetValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class ControlRestorationInfo
|
|
{
|
|
private Control _ctrl = null;
|
|
public Control Control
|
|
{
|
|
get { return _ctrl; }
|
|
}
|
|
|
|
private ControlCollection _coll = null;
|
|
public ControlCollection Collection
|
|
{
|
|
get { return _coll; }
|
|
}
|
|
|
|
public bool IsValid
|
|
{
|
|
get { return (Control != null) && (Collection != null); }
|
|
}
|
|
|
|
public ControlRestorationInfo(Control ctrl, ControlCollection coll)
|
|
{
|
|
_ctrl = ctrl;
|
|
_coll = coll;
|
|
}
|
|
|
|
public void Restore()
|
|
{
|
|
if (IsValid)
|
|
{
|
|
_coll.Add(_ctrl);
|
|
}
|
|
}
|
|
}
|
|
}
|