2 // System.Web.UI.ClientScriptManager.cs
5 // Duncan Mak (duncan@ximian.com)
6 // Gonzalo Paniagua (gonzalo@ximian.com)
7 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
8 // Lluis Sanchez (lluis@novell.com)
10 // (C) 2002,2003 Ximian, Inc. (http://www.ximian.com)
11 // (c) 2003 Novell, Inc. (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Collections;
38 using System.Collections.Generic;
41 using System.Collections.Specialized;
42 using System.Web.Util;
43 using System.Globalization;
45 namespace System.Web.UI
52 class ClientScriptManager
54 internal const string EventStateFieldName = "__EVENTVALIDATION";
56 Hashtable registeredArrayDeclares;
57 ScriptEntry clientScriptBlocks;
58 ScriptEntry startupScriptBlocks;
59 internal Hashtable hiddenFields;
60 ScriptEntry submitStatements;
63 int [] eventValidationValues;
64 int eventValidationPos = 0;
65 Hashtable expandoAttributes;
66 bool _hasRegisteredForEventValidationOnCallback;
68 bool _initCallBackRegistered;
69 bool _webFormClientScriptRendered;
70 bool _webFormClientScriptRequired;
72 internal bool ScriptsPresent {
74 return _webFormClientScriptRequired ||
75 _initCallBackRegistered ||
76 _hasRegisteredForEventValidationOnCallback ||
77 clientScriptBlocks != null ||
78 startupScriptBlocks != null ||
79 submitStatements != null ||
80 registeredArrayDeclares != null ||
81 expandoAttributes != null;
86 internal ClientScriptManager (Page page)
92 public string GetPostBackClientEvent (Control control, string argument)
94 return GetPostBackEventReference (control, argument);
98 public string GetPostBackClientHyperlink (Control control, string argument)
100 return "javascript:" + GetPostBackEventReference (control, argument);
104 public string GetPostBackClientHyperlink (Control control, string argument, bool registerForEventValidation)
106 if (registerForEventValidation)
107 RegisterForEventValidation (control.UniqueID, argument);
108 return "javascript:" + GetPostBackEventReference (control, argument);
117 string GetPostBackEventReference (Control control, string argument)
120 throw new ArgumentNullException ("control");
122 page.RequiresPostBackScript ();
124 return page.theForm + ".__doPostBack('" + control.UniqueID + "','" + argument + "')";
126 return "__doPostBack('" + control.UniqueID + "','" + argument + "')";
130 public string GetPostBackEventReference (Control control, string argument, bool registerForEventValidation)
133 throw new ArgumentNullException ("control");
135 if (registerForEventValidation)
136 RegisterForEventValidation (control.UniqueID, argument);
137 return GetPostBackEventReference (control, argument);
140 public string GetPostBackEventReference (PostBackOptions options, bool registerForEventValidation)
143 throw new ArgumentNullException ("options");
144 if (registerForEventValidation)
145 RegisterForEventValidation (options);
146 return GetPostBackEventReference (options);
149 public string GetPostBackEventReference (PostBackOptions options)
152 throw new ArgumentNullException ("options");
154 if (options.ActionUrl == null && options.ValidationGroup == null && !options.TrackFocus &&
155 !options.AutoPostBack && !options.PerformValidation)
157 if (!options.ClientSubmit)
160 if (options.RequiresJavaScriptProtocol)
161 return GetPostBackClientHyperlink (options.TargetControl, options.Argument);
163 return GetPostBackEventReference (options.TargetControl, options.Argument);
166 RegisterWebFormClientScript ();
168 string actionUrl = options.ActionUrl;
169 if (actionUrl != null)
170 RegisterHiddenField (Page.PreviousPageID, page.Request.FilePath);
172 if(options.TrackFocus)
173 RegisterHiddenField (Page.LastFocusID, String.Empty);
175 string prefix = options.RequiresJavaScriptProtocol ? "javascript:" : "";
176 if (page.IsMultiForm)
177 prefix += page.theForm + ".";
179 return prefix + "WebForm_DoPostback(" +
180 ClientScriptManager.GetScriptLiteral (options.TargetControl.UniqueID) + "," +
181 ClientScriptManager.GetScriptLiteral (options.Argument) + "," +
182 ClientScriptManager.GetScriptLiteral (actionUrl) + "," +
183 ClientScriptManager.GetScriptLiteral (options.AutoPostBack) + "," +
184 ClientScriptManager.GetScriptLiteral (options.PerformValidation) + "," +
185 ClientScriptManager.GetScriptLiteral (options.TrackFocus) + "," +
186 ClientScriptManager.GetScriptLiteral (options.ClientSubmit) + "," +
187 ClientScriptManager.GetScriptLiteral (options.ValidationGroup) + ")";
190 internal void RegisterWebFormClientScript ()
192 if (_webFormClientScriptRequired)
195 page.RequiresPostBackScript ();
196 _webFormClientScriptRequired = true;
199 internal void WriteWebFormClientScript (HtmlTextWriter writer) {
200 if (!_webFormClientScriptRendered && _webFormClientScriptRequired) {
202 WriteClientScriptInclude (writer, GetWebResourceUrl (typeof (Page), "webform.js"), typeof (Page), "webform.js");
203 WriteBeginScriptBlock (writer);
204 writer.WriteLine ("WebForm_Initialize({0});", page.IsMultiForm ? page.theForm : "window");
205 WriteEndScriptBlock (writer);
206 _webFormClientScriptRendered = true;
210 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context)
212 return GetCallbackEventReference (control, argument, clientCallback, context, null, false);
215 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, bool useAsync)
217 return GetCallbackEventReference (control, argument, clientCallback, context, null, useAsync);
220 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
223 throw new ArgumentNullException ("control");
224 if(!(control is ICallbackEventHandler))
225 throw new InvalidOperationException ("The control must implement the ICallbackEventHandler interface and provide a RaiseCallbackEvent method.");
227 return GetCallbackEventReference ("'" + control.UniqueID + "'", argument, clientCallback, context, clientErrorCallback, useAsync);
230 public string GetCallbackEventReference (string target, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
232 RegisterWebFormClientScript ();
234 if (!_initCallBackRegistered) {
235 _initCallBackRegistered = true;
236 RegisterStartupScript (typeof (Page), "WebForm_InitCallback", page.WebFormScriptReference + ".WebForm_InitCallback();", true);
238 return page.WebFormScriptReference + ".WebForm_DoCallback(" +
240 (argument ?? "null") + "," +
241 clientCallback + "," +
242 (context ?? "null") + "," +
243 (clientErrorCallback ?? "null") + "," +
244 (useAsync ? "true" : "false") + ")";
253 string GetWebResourceUrl(Type type, string resourceName)
256 throw new ArgumentNullException ("type");
258 if (resourceName == null || resourceName.Length == 0)
259 throw new ArgumentNullException ("type");
261 return System.Web.Handlers.AssemblyResourceLoader.GetResourceUrl (type, resourceName);
265 public bool IsClientScriptBlockRegistered (string key)
267 return IsScriptRegistered (clientScriptBlocks, GetType(), key);
270 public bool IsClientScriptBlockRegistered (Type type, string key)
272 return IsScriptRegistered (clientScriptBlocks, type, key);
275 public bool IsStartupScriptRegistered (string key)
277 return IsScriptRegistered (startupScriptBlocks, GetType(), key);
280 public bool IsStartupScriptRegistered (Type type, string key)
282 return IsScriptRegistered (startupScriptBlocks, type, key);
285 public bool IsOnSubmitStatementRegistered (string key)
287 return IsScriptRegistered (submitStatements, GetType(), key);
290 public bool IsOnSubmitStatementRegistered (Type type, string key)
292 return IsScriptRegistered (submitStatements, type, key);
295 public bool IsClientScriptIncludeRegistered (string key)
297 return IsClientScriptIncludeRegistered (GetType (), key);
300 public bool IsClientScriptIncludeRegistered (Type type, string key)
302 return IsScriptRegistered (clientScriptBlocks, type, "include-" + key);
305 bool IsScriptRegistered (ScriptEntry scriptList, Type type, string key)
307 while (scriptList != null) {
308 if (scriptList.Type == type && scriptList.Key == key)
310 scriptList = scriptList.Next;
315 public void RegisterArrayDeclaration (string arrayName, string arrayValue)
317 if (registeredArrayDeclares == null)
318 registeredArrayDeclares = new Hashtable();
320 if (!registeredArrayDeclares.ContainsKey (arrayName))
321 registeredArrayDeclares.Add (arrayName, new ArrayList());
323 ((ArrayList) registeredArrayDeclares[arrayName]).Add(arrayValue);
324 page.RequiresFormScriptDeclaration ();
327 void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, bool addScriptTags)
329 RegisterScript (ref scriptList, type, key, script, addScriptTags ? ScriptEntryFormat.AddScriptTag : ScriptEntryFormat.None);
332 void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, ScriptEntryFormat format)
334 ScriptEntry last = null;
335 ScriptEntry entry = scriptList;
337 while (entry != null) {
338 if (entry.Type == type && entry.Key == key)
344 entry = new ScriptEntry (type, key, script, format);
346 if (last != null) last.Next = entry;
347 else scriptList = entry;
350 internal void RegisterClientScriptBlock (string key, string script)
352 RegisterScript (ref clientScriptBlocks, GetType(), key, script, false);
355 public void RegisterClientScriptBlock (Type type, string key, string script)
357 RegisterClientScriptBlock (type, key, script, false);
360 public void RegisterClientScriptBlock (Type type, string key, string script, bool addScriptTags)
363 throw new ArgumentNullException ("type");
365 RegisterScript (ref clientScriptBlocks, type, key, script, addScriptTags);
368 public void RegisterHiddenField (string hiddenFieldName, string hiddenFieldInitialValue)
370 if (hiddenFields == null)
371 hiddenFields = new Hashtable ();
373 if (!hiddenFields.ContainsKey (hiddenFieldName))
374 hiddenFields.Add (hiddenFieldName, hiddenFieldInitialValue);
377 internal void RegisterOnSubmitStatement (string key, string script)
379 RegisterScript (ref submitStatements, GetType (), key, script, false);
382 public void RegisterOnSubmitStatement (Type type, string key, string script)
385 throw new ArgumentNullException ("type");
387 RegisterScript (ref submitStatements, type, key, script, false);
390 internal void RegisterStartupScript (string key, string script)
392 RegisterScript (ref startupScriptBlocks, GetType(), key, script, false);
395 public void RegisterStartupScript (Type type, string key, string script)
397 RegisterStartupScript (type, key, script, false);
400 public void RegisterStartupScript (Type type, string key, string script, bool addScriptTags)
403 throw new ArgumentNullException ("type");
405 RegisterScript (ref startupScriptBlocks, type, key, script, addScriptTags);
408 public void RegisterClientScriptInclude (string key, string url)
410 RegisterClientScriptInclude (GetType (), key, url);
413 public void RegisterClientScriptInclude (Type type, string key, string url)
416 throw new ArgumentNullException ("type");
417 if (url == null || url.Length == 0)
418 throw new ArgumentException ("url");
420 RegisterScript (ref clientScriptBlocks, type, "include-" + key, url, ScriptEntryFormat.Include);
424 public void RegisterClientScriptResource (Type type, string resourceName)
426 RegisterScript (ref clientScriptBlocks, type, "resource-" + resourceName, GetWebResourceUrl (type, resourceName), ScriptEntryFormat.Include);
429 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue)
431 RegisterExpandoAttribute (controlId, attributeName, attributeValue, true);
434 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue, bool encode)
436 if (controlId == null)
437 throw new ArgumentNullException ("controlId");
439 if (attributeName == null)
440 throw new ArgumentNullException ("attributeName");
442 if (expandoAttributes == null)
443 expandoAttributes = new Hashtable ();
445 ListDictionary list = (ListDictionary)expandoAttributes [controlId];
447 list = new ListDictionary ();
448 expandoAttributes [controlId] = list;
451 list.Add (attributeName, encode ? StrUtils.EscapeQuotesAndBackslashes (attributeValue) : attributeValue);
454 void EnsureEventValidationArray ()
456 if (eventValidationValues == null || eventValidationValues.Length == 0)
457 eventValidationValues = new int [64];
459 int len = eventValidationValues.Length;
461 if (eventValidationPos >= len) {
462 int [] tmp = new int [len * 2];
463 Array.Copy (eventValidationValues, tmp, len);
464 eventValidationValues = tmp;
468 internal void ResetEventValidationState ()
470 _pageInRender = true;
471 eventValidationPos = 0;
474 // Implemented following the description in http://odetocode.com/Blogs/scott/archive/2006/03/20/3145.aspx
475 int CalculateEventHash (string uniqueId, string argument)
477 int uniqueIdHash = uniqueId.GetHashCode ();
478 int argumentHash = String.IsNullOrEmpty (argument) ? 0 : argument.GetHashCode ();
479 return (uniqueIdHash ^ argumentHash);
482 public void RegisterForEventValidation (PostBackOptions options)
484 // MS.NET does not check for options == null, so we won't too...
485 RegisterForEventValidation (options.TargetControl.UniqueID, options.Argument);
488 public void RegisterForEventValidation (string uniqueId)
490 RegisterForEventValidation (uniqueId, null);
493 public void RegisterForEventValidation (string uniqueId, string argument)
495 if (!page.EnableEventValidation)
497 if (uniqueId == null || uniqueId.Length == 0)
500 _hasRegisteredForEventValidationOnCallback = true;
501 else if (!_pageInRender)
502 throw new InvalidOperationException ("RegisterForEventValidation may only be called from the Render method");
504 EnsureEventValidationArray ();
506 int hash = CalculateEventHash (uniqueId, argument);
507 for (int i = 0; i < eventValidationPos; i++)
508 if (eventValidationValues [i] == hash)
510 eventValidationValues [eventValidationPos++] = hash;
513 public void ValidateEvent (string uniqueId)
515 ValidateEvent (uniqueId, null);
518 ArgumentException InvalidPostBackException ()
520 return new ArgumentException ("Invalid postback or callback argument. Event validation is enabled using <pages enableEventValidation=\"true\"/> in configuration or <%@ Page EnableEventValidation=\"true\" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.");
523 public void ValidateEvent (string uniqueId, string argument)
525 if (uniqueId == null || uniqueId.Length == 0)
526 throw new ArgumentException ("must not be null or empty", "uniqueId");
527 if (!page.EnableEventValidation)
529 if (eventValidationValues == null)
530 throw InvalidPostBackException ();
532 int hash = CalculateEventHash (uniqueId, argument);
533 for (int i = 0; i < eventValidationValues.Length; i++)
534 if (eventValidationValues [i] == hash)
537 throw InvalidPostBackException ();
540 void WriteScripts (HtmlTextWriter writer, ScriptEntry scriptList)
542 if (scriptList == null)
547 while (scriptList != null) {
548 switch (scriptList.Format) {
549 case ScriptEntryFormat.AddScriptTag:
550 EnsureBeginScriptBlock (writer);
551 writer.Write (scriptList.Script);
553 case ScriptEntryFormat.Include:
554 EnsureEndScriptBlock (writer);
555 WriteClientScriptInclude (writer, scriptList.Script, scriptList.Type, scriptList.Key);
558 EnsureEndScriptBlock (writer);
559 writer.WriteLine (scriptList.Script);
562 scriptList = scriptList.Next;
564 EnsureEndScriptBlock (writer);
567 bool _scriptTagOpened;
569 void EnsureBeginScriptBlock (HtmlTextWriter writer) {
570 if (!_scriptTagOpened) {
571 WriteBeginScriptBlock (writer);
572 _scriptTagOpened = true;
576 void EnsureEndScriptBlock (HtmlTextWriter writer) {
577 if (_scriptTagOpened) {
578 WriteEndScriptBlock (writer);
579 _scriptTagOpened = false;
584 internal void RestoreEventValidationState (string fieldValue)
586 if (!page.EnableEventValidation || fieldValue == null || fieldValue.Length == 0)
588 IStateFormatter fmt = page.GetFormatter ();
589 eventValidationValues = (int []) fmt.Deserialize (fieldValue);
590 eventValidationPos = eventValidationValues.Length;
593 internal void SaveEventValidationState ()
595 if (!page.EnableEventValidation)
598 string eventValidation = GetEventValidationStateFormatted ();
599 if (eventValidation == null)
602 RegisterHiddenField (EventStateFieldName, eventValidation);
605 internal string GetEventValidationStateFormatted ()
607 if (eventValidationValues == null || eventValidationValues.Length == 0)
610 if(page.IsCallback && !_hasRegisteredForEventValidationOnCallback)
613 IStateFormatter fmt = page.GetFormatter ();
614 int [] array = new int [eventValidationPos];
615 Array.Copy (eventValidationValues, array, eventValidationPos);
616 return fmt.Serialize (array);
619 internal void WriteExpandoAttributes (HtmlTextWriter writer)
621 if (expandoAttributes == null)
625 WriteBeginScriptBlock (writer);
627 foreach (string controlId in expandoAttributes.Keys) {
628 writer.WriteLine ("var {0} = document.all ? document.all [\"{0}\"] : document.getElementById (\"{0}\");", controlId);
629 ListDictionary attrs = (ListDictionary) expandoAttributes [controlId];
630 foreach (string attributeName in attrs.Keys) {
631 writer.WriteLine ("{0}.{1} = \"{2}\";", controlId, attributeName, attrs [attributeName]);
634 WriteEndScriptBlock (writer);
641 internal const string SCRIPT_BLOCK_START = "//<![CDATA[";
642 internal const string SCRIPT_BLOCK_END = "//]]>";
643 internal const string SCRIPT_ELEMENT_START = @"<script type=""text/javascript"">" + SCRIPT_BLOCK_START;
645 internal const string SCRIPT_BLOCK_START = "<!--";
646 internal const string SCRIPT_BLOCK_END ="// -->";
647 internal const string SCRIPT_ELEMENT_START = @"<script language=""javascript"" type=""text/javascript"">" + SCRIPT_BLOCK_START;
649 internal const string SCRIPT_ELEMENT_END = SCRIPT_BLOCK_END + "</script>";
651 internal static void WriteBeginScriptBlock (HtmlTextWriter writer)
653 writer.WriteLine (SCRIPT_ELEMENT_START);
656 internal static void WriteEndScriptBlock (HtmlTextWriter writer)
658 writer.WriteLine (SCRIPT_ELEMENT_END);
661 internal void WriteHiddenFields (HtmlTextWriter writer)
663 if (hiddenFields == null)
668 writer.RenderBeginTag (HtmlTextWriterTag.Div);
669 int oldIndent = writer.Indent;
673 foreach (string key in hiddenFields.Keys) {
674 string value = hiddenFields [key] as string;
680 writer.Write ("<input type=\"hidden\" name=\"{0}\" id=\"{0}\" value=\"{1}\" />", key, HttpUtility.HtmlAttributeEncode (value));
682 writer.Write ("<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />", key, HttpUtility.HtmlAttributeEncode (value));
686 writer.Indent = oldIndent;
687 writer.RenderEndTag (); // DIV
693 internal void WriteClientScriptInclude (HtmlTextWriter writer, string path, Type type, string key) {
694 if (!page.IsMultiForm)
695 writer.WriteLine ("<script src=\"{0}\" type=\"text/javascript\"></script>", path);
697 string scriptKey = "inc_" + (type.FullName + key).GetHashCode ().ToString ("X");
698 writer.WriteLine ("<script type=\"text/javascript\">");
699 writer.WriteLine (SCRIPT_BLOCK_START);
700 writer.WriteLine ("if (!window.{0}) {{", scriptKey);
701 writer.WriteLine ("\twindow.{0} = true", scriptKey);
702 writer.WriteLine ("\tdocument.write('<script src=\"{0}\" type=\"text/javascript\"><\\/script>'); }}", path);
703 writer.WriteLine (SCRIPT_BLOCK_END);
704 writer.WriteLine ("</script>");
708 internal void WriteClientScriptBlocks (HtmlTextWriter writer)
710 WriteScripts (writer, clientScriptBlocks);
713 internal void WriteStartupScriptBlocks (HtmlTextWriter writer)
715 WriteScripts (writer, startupScriptBlocks);
718 internal void WriteArrayDeclares (HtmlTextWriter writer)
720 if (registeredArrayDeclares != null) {
722 WriteBeginScriptBlock (writer);
723 IDictionaryEnumerator arrayEnum = registeredArrayDeclares.GetEnumerator();
724 while (arrayEnum.MoveNext()) {
725 if (page.IsMultiForm)
726 writer.Write ("\t" + page.theForm + ".");
728 writer.Write ("\tvar ");
729 writer.Write(arrayEnum.Key);
730 writer.Write(" = new Array(");
731 IEnumerator arrayListEnum = ((ArrayList) arrayEnum.Value).GetEnumerator();
733 while (arrayListEnum.MoveNext()) {
738 writer.Write(arrayListEnum.Current);
740 writer.WriteLine(");");
742 WriteEndScriptBlock (writer);
748 internal string GetClientValidationEvent (string validationGroup) {
749 if (page.IsMultiForm)
750 return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate('" + validationGroup + "');";
751 return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate('" + validationGroup + "');";
755 internal string GetClientValidationEvent ()
757 if (page.IsMultiForm)
758 return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate();";
759 return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate();";
763 internal string WriteSubmitStatements ()
765 if (submitStatements == null) return null;
767 StringBuilder sb = new StringBuilder ();
768 ScriptEntry entry = submitStatements;
769 while (entry != null) {
771 sb.Append (EnsureEndsWithSemicolon (entry.Script));
773 sb.Append (entry.Script);
778 RegisterClientScriptBlock (GetType(), "HtmlForm-OnSubmitStatemen",
780 " + page.WebFormScriptReference + @".WebForm_OnSubmit = function () {
781 " + sb.ToString () + @"
785 return "javascript:return " + page.WebFormScriptReference + ".WebForm_OnSubmit();";
788 return sb.ToString ();
792 internal static string GetScriptLiteral (object ob)
796 else if (ob is string) {
797 string s = (string)ob;
801 for (int i = 0; i < len; i++)
802 if (s [i] == '\\' || s [i] == '\"') {
808 return string.Concat ("\"", s, "\"");
810 StringBuilder sb = new StringBuilder (len + 10);
813 for (int si = 0; si < len; si++) {
816 else if (s [si] == '\\')
823 return sb.ToString ();
824 } else if (ob is bool) {
825 return ob.ToString ().ToLower (Helpers.InvariantCulture);
827 return ob.ToString ();
831 sealed class ScriptEntry
833 public readonly Type Type;
834 public readonly string Key;
835 public readonly string Script;
836 public readonly ScriptEntryFormat Format;
837 public ScriptEntry Next;
839 public ScriptEntry (Type type, string key, string script, ScriptEntryFormat format) {
847 enum ScriptEntryFormat
856 internal static string EnsureEndsWithSemicolon (string value) {
857 if (value != null && value.Length > 0 && value [value.Length - 1] != ';')