392 lines
14 KiB
C#
392 lines
14 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="ScriptComponentDescriptor.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.Web.UI {
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Collections.ObjectModel;
|
||
|
using System.Diagnostics;
|
||
|
using System.Diagnostics.CodeAnalysis;
|
||
|
using System.Globalization;
|
||
|
using System.Text;
|
||
|
using System.Text.RegularExpressions;
|
||
|
using System.Web;
|
||
|
using System.Web.Resources;
|
||
|
using System.Web.Script.Serialization;
|
||
|
|
||
|
public class ScriptComponentDescriptor : ScriptDescriptor {
|
||
|
// PERF: In the ScriptControl/Properties/Scenario.aspx perf test, SortedList is 2% faster than
|
||
|
// SortedDictionary.
|
||
|
private string _elementIDInternal;
|
||
|
private SortedList<string, string> _events;
|
||
|
private string _id;
|
||
|
private SortedList<string, Expression> _properties;
|
||
|
private bool _registerDispose = true;
|
||
|
private JavaScriptSerializer _serializer;
|
||
|
private string _type;
|
||
|
|
||
|
public ScriptComponentDescriptor(string type) {
|
||
|
if (String.IsNullOrEmpty(type)) {
|
||
|
throw new ArgumentException(AtlasWeb.Common_NullOrEmpty, "type");
|
||
|
}
|
||
|
_type = type;
|
||
|
}
|
||
|
|
||
|
// Used by ScriptBehaviorDescriptor and ScriptControlDesriptor
|
||
|
internal ScriptComponentDescriptor(string type, string elementID)
|
||
|
: this(type) {
|
||
|
if (String.IsNullOrEmpty(elementID)) {
|
||
|
throw new ArgumentException(AtlasWeb.Common_NullOrEmpty, "elementID");
|
||
|
}
|
||
|
_elementIDInternal = elementID;
|
||
|
}
|
||
|
|
||
|
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID")]
|
||
|
public virtual string ClientID {
|
||
|
get {
|
||
|
return ID;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal string ElementIDInternal {
|
||
|
get {
|
||
|
return _elementIDInternal;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private SortedList<string, string> Events {
|
||
|
get {
|
||
|
if (_events == null) {
|
||
|
_events = new SortedList<string, string>(StringComparer.Ordinal);
|
||
|
}
|
||
|
return _events;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID")]
|
||
|
public virtual string ID {
|
||
|
get {
|
||
|
return _id ?? String.Empty;
|
||
|
}
|
||
|
set {
|
||
|
_id = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private SortedList<string, Expression> Properties {
|
||
|
get {
|
||
|
if (_properties == null) {
|
||
|
_properties = new SortedList<string, Expression>(StringComparer.Ordinal);
|
||
|
}
|
||
|
return _properties;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal bool RegisterDispose {
|
||
|
get {
|
||
|
return _registerDispose;
|
||
|
}
|
||
|
set {
|
||
|
_registerDispose = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private JavaScriptSerializer Serializer {
|
||
|
get {
|
||
|
if (_serializer == null) {
|
||
|
_serializer = new JavaScriptSerializer();
|
||
|
}
|
||
|
return _serializer;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods",
|
||
|
Justification = "Refers to a script element, not to Object.GetType()")]
|
||
|
public string Type {
|
||
|
get {
|
||
|
return _type;
|
||
|
}
|
||
|
set {
|
||
|
if (String.IsNullOrEmpty(value)) {
|
||
|
throw new ArgumentException(AtlasWeb.Common_NullOrEmpty, "value");
|
||
|
}
|
||
|
_type = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID")]
|
||
|
public void AddComponentProperty(string name, string componentID) {
|
||
|
if (String.IsNullOrEmpty(componentID)) {
|
||
|
throw new ArgumentException(AtlasWeb.Common_NullOrEmpty, "componentID");
|
||
|
}
|
||
|
AddProperty(name, new ComponentReference(componentID));
|
||
|
}
|
||
|
|
||
|
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID")]
|
||
|
public void AddElementProperty(string name, string elementID) {
|
||
|
if (String.IsNullOrEmpty(elementID)) {
|
||
|
throw new ArgumentException(AtlasWeb.Common_NullOrEmpty, "elementID");
|
||
|
}
|
||
|
AddProperty(name, new ElementReference(elementID));
|
||
|
}
|
||
|
|
||
|
public void AddEvent(string name, string handler) {
|
||
|
if (String.IsNullOrEmpty(name)) {
|
||
|
throw new ArgumentException(AtlasWeb.Common_NullOrEmpty, "name");
|
||
|
}
|
||
|
if (String.IsNullOrEmpty(handler)) {
|
||
|
throw new ArgumentException(AtlasWeb.Common_NullOrEmpty, "handler");
|
||
|
}
|
||
|
Events[name] = handler;
|
||
|
}
|
||
|
|
||
|
public void AddProperty(string name, object value) {
|
||
|
AddProperty(name, new ObjectReference(value));
|
||
|
}
|
||
|
|
||
|
private void AddProperty(string name, Expression value) {
|
||
|
if (String.IsNullOrEmpty(name)) {
|
||
|
throw new ArgumentException(AtlasWeb.Common_NullOrEmpty, "name");
|
||
|
}
|
||
|
Debug.Assert(value != null);
|
||
|
Properties[name] = value;
|
||
|
}
|
||
|
|
||
|
public void AddScriptProperty(string name, string script) {
|
||
|
if (String.IsNullOrEmpty(script)) {
|
||
|
throw new ArgumentException(AtlasWeb.Common_NullOrEmpty, "script");
|
||
|
}
|
||
|
AddProperty(name, new ScriptExpression(script));
|
||
|
}
|
||
|
|
||
|
private void AppendEventsScript(StringBuilder builder) {
|
||
|
// PERF: Use field directly to avoid creating Dictionary if not already created
|
||
|
if (_events != null && _events.Count > 0) {
|
||
|
builder.Append('{');
|
||
|
bool first = true;
|
||
|
|
||
|
// Can't use JavaScriptSerializer directly on events dictionary, since the values are
|
||
|
// JavaScript functions and should not be quoted.
|
||
|
foreach (KeyValuePair<string, string> e in _events) {
|
||
|
if (first) {
|
||
|
first = false;
|
||
|
}
|
||
|
else {
|
||
|
builder.Append(',');
|
||
|
}
|
||
|
builder.Append('"');
|
||
|
builder.Append(HttpUtility.JavaScriptStringEncode(e.Key));
|
||
|
builder.Append('"');
|
||
|
builder.Append(':');
|
||
|
builder.Append(e.Value);
|
||
|
}
|
||
|
|
||
|
builder.Append("}");
|
||
|
}
|
||
|
else {
|
||
|
builder.Append("null");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void AppendPropertiesScript(StringBuilder builder) {
|
||
|
bool first = true;
|
||
|
|
||
|
// PERF: Use field directly to avoid creating Dictionary if not already created
|
||
|
if (_properties != null && _properties.Count > 0) {
|
||
|
foreach (KeyValuePair<string, Expression> p in _properties) {
|
||
|
if (p.Value.Type == ExpressionType.Script) {
|
||
|
if (first) {
|
||
|
builder.Append("{");
|
||
|
first = false;
|
||
|
}
|
||
|
else {
|
||
|
builder.Append(",");
|
||
|
}
|
||
|
builder.Append('"');
|
||
|
builder.Append(HttpUtility.JavaScriptStringEncode(p.Key));
|
||
|
builder.Append('"');
|
||
|
builder.Append(':');
|
||
|
p.Value.AppendValue(Serializer, builder);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (first) {
|
||
|
// If we didn't see any properties, append "null"
|
||
|
builder.Append("null");
|
||
|
}
|
||
|
else {
|
||
|
// Else, close the JSON object
|
||
|
builder.Append("}");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void AppendReferencesScript(StringBuilder builder) {
|
||
|
bool first = true;
|
||
|
|
||
|
// PERF: Use field directly to avoid creating Dictionary if not already created
|
||
|
if (_properties != null && _properties.Count > 0) {
|
||
|
foreach (KeyValuePair<string, Expression> p in _properties) {
|
||
|
if (p.Value.Type == ExpressionType.ComponentReference) {
|
||
|
if (first) {
|
||
|
builder.Append("{");
|
||
|
first = false;
|
||
|
}
|
||
|
else {
|
||
|
builder.Append(",");
|
||
|
}
|
||
|
builder.Append('"');
|
||
|
builder.Append(HttpUtility.JavaScriptStringEncode(p.Key));
|
||
|
builder.Append('"');
|
||
|
builder.Append(':');
|
||
|
p.Value.AppendValue(Serializer, builder);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (first) {
|
||
|
// If we didn't see any references, append "null"
|
||
|
builder.Append("null");
|
||
|
}
|
||
|
else {
|
||
|
// Else, close the JSON object
|
||
|
builder.Append("}");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected internal override string GetScript() {
|
||
|
const string separator = ", ";
|
||
|
|
||
|
if (!String.IsNullOrEmpty(ID)) {
|
||
|
AddProperty("id", ID);
|
||
|
}
|
||
|
|
||
|
StringBuilder builder = new StringBuilder();
|
||
|
builder.Append("$create(");
|
||
|
|
||
|
builder.Append(Type);
|
||
|
|
||
|
builder.Append(separator);
|
||
|
AppendPropertiesScript(builder);
|
||
|
|
||
|
builder.Append(separator);
|
||
|
AppendEventsScript(builder);
|
||
|
|
||
|
builder.Append(separator);
|
||
|
AppendReferencesScript(builder);
|
||
|
|
||
|
if (ElementIDInternal != null) {
|
||
|
builder.Append(separator);
|
||
|
builder.Append("$get(\"");
|
||
|
builder.Append(HttpUtility.JavaScriptStringEncode(ElementIDInternal));
|
||
|
builder.Append("\")");
|
||
|
}
|
||
|
|
||
|
builder.Append(");");
|
||
|
|
||
|
return builder.ToString();
|
||
|
}
|
||
|
|
||
|
internal override void RegisterDisposeForDescriptor(ScriptManager scriptManager, Control owner) {
|
||
|
if (RegisterDispose && scriptManager.SupportsPartialRendering) {
|
||
|
// If partial rendering is supported, register a JavaScript statement
|
||
|
// that will dispose this component if the UpdatePanel it is inside is
|
||
|
// getting refreshed. Only components need this; controls are associated
|
||
|
// with DOM elements so they get disposed through their 'dispose' expando.
|
||
|
scriptManager.RegisterDispose(owner, "$find('" + ID + "').dispose();");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private abstract class Expression {
|
||
|
public abstract ExpressionType Type { get; }
|
||
|
public abstract void AppendValue(JavaScriptSerializer serializer, StringBuilder builder);
|
||
|
}
|
||
|
|
||
|
private enum ExpressionType {
|
||
|
Script,
|
||
|
ComponentReference
|
||
|
}
|
||
|
|
||
|
private sealed class ComponentReference : Expression {
|
||
|
private string _componentID;
|
||
|
|
||
|
public ComponentReference(string componentID) {
|
||
|
_componentID = componentID;
|
||
|
}
|
||
|
|
||
|
public override ExpressionType Type {
|
||
|
get {
|
||
|
return ExpressionType.ComponentReference;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void AppendValue(JavaScriptSerializer serializer, StringBuilder builder) {
|
||
|
builder.Append('"');
|
||
|
builder.Append(HttpUtility.JavaScriptStringEncode(_componentID));
|
||
|
builder.Append('"');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private sealed class ElementReference : Expression {
|
||
|
private string _elementID;
|
||
|
|
||
|
public ElementReference(string elementID) {
|
||
|
_elementID = elementID;
|
||
|
}
|
||
|
|
||
|
public override ExpressionType Type {
|
||
|
get {
|
||
|
return ExpressionType.Script;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void AppendValue(JavaScriptSerializer serializer, StringBuilder builder) {
|
||
|
builder.Append("$get(\"");
|
||
|
builder.Append(HttpUtility.JavaScriptStringEncode(_elementID));
|
||
|
builder.Append("\")");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private sealed class ObjectReference : Expression {
|
||
|
private object _value;
|
||
|
|
||
|
public ObjectReference(object value) {
|
||
|
_value = value;
|
||
|
}
|
||
|
|
||
|
public override ExpressionType Type {
|
||
|
get {
|
||
|
return ExpressionType.Script;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void AppendValue(JavaScriptSerializer serializer, StringBuilder builder) {
|
||
|
// DevDiv Bugs 96574: pass SerializationFormat.JavaScript to serialize to straight JavaScript,
|
||
|
// so date properties are rendered as dates
|
||
|
serializer.Serialize(_value, builder, JavaScriptSerializer.SerializationFormat.JavaScript);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private sealed class ScriptExpression : Expression {
|
||
|
private string _script;
|
||
|
|
||
|
public ScriptExpression(string script) {
|
||
|
_script = script;
|
||
|
}
|
||
|
|
||
|
public override ExpressionType Type {
|
||
|
get {
|
||
|
return ExpressionType.Script;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void AppendValue(JavaScriptSerializer serializer, StringBuilder builder) {
|
||
|
builder.Append(_script);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|