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-2010 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;
37 using System.Collections.Generic;
39 using System.Collections.Specialized;
40 using System.Web.Util;
41 using System.Globalization;
43 namespace System.Web.UI
45 public sealed partial class ClientScriptManager
47 internal const string EventStateFieldName = "__EVENTVALIDATION";
49 Hashtable registeredArrayDeclares;
50 ScriptEntry clientScriptBlocks;
51 ScriptEntry startupScriptBlocks;
52 internal Hashtable hiddenFields;
53 ScriptEntry submitStatements;
55 int [] eventValidationValues;
56 int eventValidationPos = 0;
57 Hashtable expandoAttributes;
58 bool _hasRegisteredForEventValidationOnCallback;
60 bool _initCallBackRegistered;
61 bool _webFormClientScriptRendered;
62 bool _webFormClientScriptRequired;
64 internal bool ScriptsPresent {
66 return _webFormClientScriptRequired ||
67 _initCallBackRegistered ||
68 _hasRegisteredForEventValidationOnCallback ||
69 clientScriptBlocks != null ||
70 startupScriptBlocks != null ||
71 submitStatements != null ||
72 registeredArrayDeclares != null ||
73 expandoAttributes != null;
77 internal ClientScriptManager (Page page)
82 public string GetPostBackClientHyperlink (Control control, string argument)
84 return "javascript:" + GetPostBackEventReference (control, argument);
87 public string GetPostBackClientHyperlink (Control control, string argument, bool registerForEventValidation)
89 if (registerForEventValidation)
90 RegisterForEventValidation (control.UniqueID, argument);
91 return "javascript:" + GetPostBackEventReference (control, argument);
94 public string GetPostBackEventReference (Control control, string argument)
97 throw new ArgumentNullException ("control");
99 page.RequiresPostBackScript ();
101 return page.theForm + ".__doPostBack('" + control.UniqueID + "','" + argument + "')";
103 return "__doPostBack('" + control.UniqueID + "','" + argument + "')";
106 public string GetPostBackEventReference (Control control, string argument, bool registerForEventValidation)
109 throw new ArgumentNullException ("control");
111 if (registerForEventValidation)
112 RegisterForEventValidation (control.UniqueID, argument);
113 return GetPostBackEventReference (control, argument);
116 public string GetPostBackEventReference (PostBackOptions options, bool registerForEventValidation)
119 throw new ArgumentNullException ("options");
120 if (registerForEventValidation)
121 RegisterForEventValidation (options);
122 return GetPostBackEventReference (options);
125 public string GetPostBackEventReference (PostBackOptions options)
128 throw new ArgumentNullException ("options");
130 if (options.ActionUrl == null && options.ValidationGroup == null && !options.TrackFocus &&
131 !options.AutoPostBack && !options.PerformValidation)
133 if (!options.ClientSubmit)
136 if (options.RequiresJavaScriptProtocol)
137 return GetPostBackClientHyperlink (options.TargetControl, options.Argument);
139 return GetPostBackEventReference (options.TargetControl, options.Argument);
142 RegisterWebFormClientScript ();
144 string actionUrl = options.ActionUrl;
145 if (actionUrl != null)
146 RegisterHiddenField (Page.PreviousPageID, page.Request.FilePath);
148 if(options.TrackFocus)
149 RegisterHiddenField (Page.LastFocusID, String.Empty);
151 string prefix = options.RequiresJavaScriptProtocol ? "javascript:" : String.Empty;
152 if (page.IsMultiForm)
153 prefix += page.theForm + ".";
155 return prefix + "WebForm_DoPostback(" +
156 ClientScriptManager.GetScriptLiteral (options.TargetControl.UniqueID) + "," +
157 ClientScriptManager.GetScriptLiteral (options.Argument) + "," +
158 ClientScriptManager.GetScriptLiteral (actionUrl) + "," +
159 ClientScriptManager.GetScriptLiteral (options.AutoPostBack) + "," +
160 ClientScriptManager.GetScriptLiteral (options.PerformValidation) + "," +
161 ClientScriptManager.GetScriptLiteral (options.TrackFocus) + "," +
162 ClientScriptManager.GetScriptLiteral (options.ClientSubmit) + "," +
163 ClientScriptManager.GetScriptLiteral (options.ValidationGroup) + ")";
166 internal void RegisterWebFormClientScript ()
168 if (_webFormClientScriptRequired)
171 page.RequiresPostBackScript ();
172 _webFormClientScriptRequired = true;
175 internal void WriteWebFormClientScript (HtmlTextWriter writer) {
176 if (!_webFormClientScriptRendered && _webFormClientScriptRequired) {
178 WriteClientScriptInclude (writer, GetWebResourceUrl (typeof (Page), "webform.js"), typeof (Page), "webform.js");
179 WriteBeginScriptBlock (writer);
180 writer.WriteLine ("WebForm_Initialize({0});", page.IsMultiForm ? page.theForm : "window");
181 WriteEndScriptBlock (writer);
182 _webFormClientScriptRendered = true;
186 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context)
188 return GetCallbackEventReference (control, argument, clientCallback, context, null, false);
191 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, bool useAsync)
193 return GetCallbackEventReference (control, argument, clientCallback, context, null, useAsync);
196 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
199 throw new ArgumentNullException ("control");
200 if(!(control is ICallbackEventHandler))
201 throw new InvalidOperationException ("The control must implement the ICallbackEventHandler interface and provide a RaiseCallbackEvent method.");
203 return GetCallbackEventReference ("'" + control.UniqueID + "'", argument, clientCallback, context, clientErrorCallback, useAsync);
206 public string GetCallbackEventReference (string target, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
208 RegisterWebFormClientScript ();
210 if (!_initCallBackRegistered) {
211 _initCallBackRegistered = true;
212 RegisterStartupScript (typeof (Page), "WebForm_InitCallback", page.WebFormScriptReference + ".WebForm_InitCallback();", true);
214 return page.WebFormScriptReference + ".WebForm_DoCallback(" +
216 (argument ?? "null") + "," +
217 clientCallback + "," +
218 (context ?? "null") + "," +
219 (clientErrorCallback ?? "null") + "," +
220 (useAsync ? "true" : "false") + ")";
223 public string GetWebResourceUrl(Type type, string resourceName)
226 throw new ArgumentNullException ("type");
228 if (resourceName == null || resourceName.Length == 0)
229 throw new ArgumentNullException ("type");
231 return System.Web.Handlers.AssemblyResourceLoader.GetResourceUrl (type, resourceName);
235 public bool IsClientScriptBlockRegistered (string key)
237 return IsScriptRegistered (clientScriptBlocks, GetType(), key);
240 public bool IsClientScriptBlockRegistered (Type type, string key)
242 return IsScriptRegistered (clientScriptBlocks, type, key);
245 public bool IsStartupScriptRegistered (string key)
247 return IsScriptRegistered (startupScriptBlocks, GetType(), key);
250 public bool IsStartupScriptRegistered (Type type, string key)
252 return IsScriptRegistered (startupScriptBlocks, type, key);
255 public bool IsOnSubmitStatementRegistered (string key)
257 return IsScriptRegistered (submitStatements, GetType(), key);
260 public bool IsOnSubmitStatementRegistered (Type type, string key)
262 return IsScriptRegistered (submitStatements, type, key);
265 public bool IsClientScriptIncludeRegistered (string key)
267 return IsClientScriptIncludeRegistered (GetType (), key);
270 public bool IsClientScriptIncludeRegistered (Type type, string key)
272 return IsScriptRegistered (clientScriptBlocks, type, "include-" + key);
275 bool IsScriptRegistered (ScriptEntry scriptList, Type type, string key)
277 while (scriptList != null) {
278 if (scriptList.Type == type && scriptList.Key == key)
280 scriptList = scriptList.Next;
285 public void RegisterArrayDeclaration (string arrayName, string arrayValue)
287 if (registeredArrayDeclares == null)
288 registeredArrayDeclares = new Hashtable();
290 if (!registeredArrayDeclares.ContainsKey (arrayName))
291 registeredArrayDeclares.Add (arrayName, new ArrayList());
293 ((ArrayList) registeredArrayDeclares[arrayName]).Add(arrayValue);
294 page.RequiresFormScriptDeclaration ();
297 void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, bool addScriptTags)
299 RegisterScript (ref scriptList, type, key, script, addScriptTags ? ScriptEntryFormat.AddScriptTag : ScriptEntryFormat.None);
302 void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, ScriptEntryFormat format)
304 ScriptEntry last = null;
305 ScriptEntry entry = scriptList;
307 while (entry != null) {
308 if (entry.Type == type && entry.Key == key)
314 entry = new ScriptEntry (type, key, script, format);
316 if (last != null) last.Next = entry;
317 else scriptList = entry;
320 internal void RegisterClientScriptBlock (string key, string script)
322 RegisterScript (ref clientScriptBlocks, GetType(), key, script, false);
325 public void RegisterClientScriptBlock (Type type, string key, string script)
327 RegisterClientScriptBlock (type, key, script, false);
330 public void RegisterClientScriptBlock (Type type, string key, string script, bool addScriptTags)
333 throw new ArgumentNullException ("type");
335 RegisterScript (ref clientScriptBlocks, type, key, script, addScriptTags);
338 public void RegisterHiddenField (string hiddenFieldName, string hiddenFieldInitialValue)
340 if (hiddenFields == null)
341 hiddenFields = new Hashtable ();
343 if (!hiddenFields.ContainsKey (hiddenFieldName))
344 hiddenFields.Add (hiddenFieldName, hiddenFieldInitialValue);
347 internal void RegisterOnSubmitStatement (string key, string script)
349 RegisterScript (ref submitStatements, GetType (), key, script, false);
352 public void RegisterOnSubmitStatement (Type type, string key, string script)
355 throw new ArgumentNullException ("type");
357 RegisterScript (ref submitStatements, type, key, script, false);
360 internal void RegisterStartupScript (string key, string script)
362 RegisterScript (ref startupScriptBlocks, GetType(), key, script, false);
365 public void RegisterStartupScript (Type type, string key, string script)
367 RegisterStartupScript (type, key, script, false);
370 public void RegisterStartupScript (Type type, string key, string script, bool addScriptTags)
373 throw new ArgumentNullException ("type");
375 RegisterScript (ref startupScriptBlocks, type, key, script, addScriptTags);
378 public void RegisterClientScriptInclude (string key, string url)
380 RegisterClientScriptInclude (GetType (), key, url);
383 public void RegisterClientScriptInclude (Type type, string key, string url)
386 throw new ArgumentNullException ("type");
387 if (url == null || url.Length == 0)
388 throw new ArgumentException ("url");
390 RegisterScript (ref clientScriptBlocks, type, "include-" + key, url, ScriptEntryFormat.Include);
393 public void RegisterClientScriptResource (Type type, string resourceName)
395 RegisterScript (ref clientScriptBlocks, type, "resource-" + resourceName, GetWebResourceUrl (type, resourceName), ScriptEntryFormat.Include);
398 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue)
400 RegisterExpandoAttribute (controlId, attributeName, attributeValue, true);
403 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue, bool encode)
405 if (controlId == null)
406 throw new ArgumentNullException ("controlId");
408 if (attributeName == null)
409 throw new ArgumentNullException ("attributeName");
411 if (expandoAttributes == null)
412 expandoAttributes = new Hashtable ();
414 ListDictionary list = (ListDictionary)expandoAttributes [controlId];
416 list = new ListDictionary ();
417 expandoAttributes [controlId] = list;
420 list.Add (attributeName, encode ? StrUtils.EscapeQuotesAndBackslashes (attributeValue) : attributeValue);
423 void EnsureEventValidationArray ()
425 if (eventValidationValues == null || eventValidationValues.Length == 0)
426 eventValidationValues = new int [64];
428 int len = eventValidationValues.Length;
430 if (eventValidationPos >= len) {
431 int [] tmp = new int [len * 2];
432 Array.Copy (eventValidationValues, tmp, len);
433 eventValidationValues = tmp;
437 internal void ResetEventValidationState ()
439 _pageInRender = true;
440 eventValidationPos = 0;
443 // Implemented following the description in http://odetocode.com/Blogs/scott/archive/2006/03/20/3145.aspx
444 int CalculateEventHash (string uniqueId, string argument)
446 int uniqueIdHash = uniqueId.GetHashCode ();
447 int argumentHash = String.IsNullOrEmpty (argument) ? 0 : argument.GetHashCode ();
448 return (uniqueIdHash ^ argumentHash);
451 public void RegisterForEventValidation (PostBackOptions options)
453 // MS.NET does not check for options == null, so we won't too...
454 RegisterForEventValidation (options.TargetControl.UniqueID, options.Argument);
457 public void RegisterForEventValidation (string uniqueId)
459 RegisterForEventValidation (uniqueId, null);
462 public void RegisterForEventValidation (string uniqueId, string argument)
464 if (!page.EnableEventValidation)
466 if (uniqueId == null || uniqueId.Length == 0)
469 _hasRegisteredForEventValidationOnCallback = true;
470 else if (!_pageInRender)
471 throw new InvalidOperationException ("RegisterForEventValidation may only be called from the Render method");
473 EnsureEventValidationArray ();
475 int hash = CalculateEventHash (uniqueId, argument);
476 for (int i = 0; i < eventValidationPos; i++)
477 if (eventValidationValues [i] == hash)
479 eventValidationValues [eventValidationPos++] = hash;
482 public void ValidateEvent (string uniqueId)
484 ValidateEvent (uniqueId, null);
487 ArgumentException InvalidPostBackException ()
489 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.");
492 public void ValidateEvent (string uniqueId, string argument)
494 if (uniqueId == null || uniqueId.Length == 0)
495 throw new ArgumentException ("must not be null or empty", "uniqueId");
496 if (!page.EnableEventValidation)
498 if (eventValidationValues == null)
499 throw InvalidPostBackException ();
501 int hash = CalculateEventHash (uniqueId, argument);
502 for (int i = 0; i < eventValidationValues.Length; i++)
503 if (eventValidationValues [i] == hash)
506 throw InvalidPostBackException ();
509 void WriteScripts (HtmlTextWriter writer, ScriptEntry scriptList)
511 if (scriptList == null)
516 while (scriptList != null) {
517 switch (scriptList.Format) {
518 case ScriptEntryFormat.AddScriptTag:
519 EnsureBeginScriptBlock (writer);
520 writer.Write (scriptList.Script);
522 case ScriptEntryFormat.Include:
523 EnsureEndScriptBlock (writer);
524 WriteClientScriptInclude (writer, scriptList.Script, scriptList.Type, scriptList.Key);
527 EnsureEndScriptBlock (writer);
528 writer.WriteLine (scriptList.Script);
531 scriptList = scriptList.Next;
533 EnsureEndScriptBlock (writer);
536 bool _scriptTagOpened;
538 void EnsureBeginScriptBlock (HtmlTextWriter writer) {
539 if (!_scriptTagOpened) {
540 WriteBeginScriptBlock (writer);
541 _scriptTagOpened = true;
545 void EnsureEndScriptBlock (HtmlTextWriter writer) {
546 if (_scriptTagOpened) {
547 WriteEndScriptBlock (writer);
548 _scriptTagOpened = false;
552 internal void RestoreEventValidationState (string fieldValue)
554 if (!page.EnableEventValidation || fieldValue == null || fieldValue.Length == 0)
556 IStateFormatter fmt = page.GetFormatter ();
557 eventValidationValues = (int []) fmt.Deserialize (fieldValue);
558 eventValidationPos = eventValidationValues.Length;
561 internal void SaveEventValidationState ()
563 if (!page.EnableEventValidation)
566 string eventValidation = GetEventValidationStateFormatted ();
567 if (eventValidation == null)
570 RegisterHiddenField (EventStateFieldName, eventValidation);
573 internal string GetEventValidationStateFormatted ()
575 if (eventValidationValues == null || eventValidationValues.Length == 0)
578 if(page.IsCallback && !_hasRegisteredForEventValidationOnCallback)
581 IStateFormatter fmt = page.GetFormatter ();
582 int [] array = new int [eventValidationPos];
583 Array.Copy (eventValidationValues, array, eventValidationPos);
584 return fmt.Serialize (array);
587 internal void WriteExpandoAttributes (HtmlTextWriter writer)
589 if (expandoAttributes == null)
593 WriteBeginScriptBlock (writer);
595 foreach (string controlId in expandoAttributes.Keys) {
596 writer.WriteLine ("var {0} = document.all ? document.all [\"{0}\"] : document.getElementById (\"{0}\");", controlId);
597 ListDictionary attrs = (ListDictionary) expandoAttributes [controlId];
598 foreach (string attributeName in attrs.Keys) {
599 writer.WriteLine ("{0}.{1} = \"{2}\";", controlId, attributeName, attrs [attributeName]);
602 WriteEndScriptBlock (writer);
606 internal const string SCRIPT_BLOCK_START = "//<![CDATA[";
607 internal const string SCRIPT_BLOCK_END = "//]]>";
608 internal const string SCRIPT_ELEMENT_START = @"<script type=""text/javascript"">" + SCRIPT_BLOCK_START;
609 internal const string SCRIPT_ELEMENT_END = SCRIPT_BLOCK_END + "</script>";
611 internal static void WriteBeginScriptBlock (HtmlTextWriter writer)
613 writer.WriteLine (SCRIPT_ELEMENT_START);
616 internal static void WriteEndScriptBlock (HtmlTextWriter writer)
618 writer.WriteLine (SCRIPT_ELEMENT_END);
621 internal void WriteHiddenFields (HtmlTextWriter writer)
623 if (hiddenFields == null)
628 writer.AddAttribute (HtmlTextWriterAttribute.Class, "aspNetHidden");
630 writer.RenderBeginTag (HtmlTextWriterTag.Div);
631 int oldIndent = writer.Indent;
635 foreach (string key in hiddenFields.Keys) {
636 string value = hiddenFields [key] as string;
641 writer.Write ("<input type=\"hidden\" name=\"{0}\" id=\"{0}\" value=\"{1}\" />", key, HttpUtility.HtmlAttributeEncode (value));
643 writer.Indent = oldIndent;
644 writer.RenderEndTag (); // DIV
649 internal void WriteClientScriptInclude (HtmlTextWriter writer, string path, Type type, string key) {
650 if (!page.IsMultiForm)
651 writer.WriteLine ("<script src=\"{0}\" type=\"text/javascript\"></script>", path);
653 string scriptKey = "inc_" + (type.FullName + key).GetHashCode ().ToString ("X");
654 writer.WriteLine ("<script type=\"text/javascript\">");
655 writer.WriteLine (SCRIPT_BLOCK_START);
656 writer.WriteLine ("if (!window.{0}) {{", scriptKey);
657 writer.WriteLine ("\twindow.{0} = true", scriptKey);
658 writer.WriteLine ("\tdocument.write('<script src=\"{0}\" type=\"text/javascript\"><\\/script>'); }}", path);
659 writer.WriteLine (SCRIPT_BLOCK_END);
660 writer.WriteLine ("</script>");
664 internal void WriteClientScriptBlocks (HtmlTextWriter writer)
666 WriteScripts (writer, clientScriptBlocks);
669 internal void WriteStartupScriptBlocks (HtmlTextWriter writer)
671 WriteScripts (writer, startupScriptBlocks);
674 internal void WriteArrayDeclares (HtmlTextWriter writer)
676 if (registeredArrayDeclares != null) {
678 WriteBeginScriptBlock (writer);
679 IDictionaryEnumerator arrayEnum = registeredArrayDeclares.GetEnumerator();
680 while (arrayEnum.MoveNext()) {
681 if (page.IsMultiForm)
682 writer.Write ("\t" + page.theForm + ".");
684 writer.Write ("\tvar ");
685 writer.Write(arrayEnum.Key);
686 writer.Write(" = new Array(");
687 IEnumerator arrayListEnum = ((ArrayList) arrayEnum.Value).GetEnumerator();
689 while (arrayListEnum.MoveNext()) {
694 writer.Write(arrayListEnum.Current);
696 writer.WriteLine(");");
698 WriteEndScriptBlock (writer);
703 internal string GetClientValidationEvent (string validationGroup) {
704 if (page.IsMultiForm)
705 return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate('" + validationGroup + "');";
706 return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate('" + validationGroup + "');";
709 internal string GetClientValidationEvent ()
711 if (page.IsMultiForm)
712 return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate();";
713 return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate();";
717 internal string WriteSubmitStatements ()
719 if (submitStatements == null) return null;
721 StringBuilder sb = new StringBuilder ();
722 ScriptEntry entry = submitStatements;
723 while (entry != null) {
724 sb.Append (EnsureEndsWithSemicolon (entry.Script));
727 RegisterClientScriptBlock (GetType(), "HtmlForm-OnSubmitStatemen",
729 " + page.WebFormScriptReference + @".WebForm_OnSubmit = function () {
730 " + sb.ToString () + @"
734 return "javascript:return " + page.WebFormScriptReference + ".WebForm_OnSubmit();";
737 internal static string GetScriptLiteral (object ob)
741 else if (ob is string) {
742 string s = (string)ob;
746 for (int i = 0; i < len; i++)
747 if (s [i] == '\\' || s [i] == '\"') {
753 return string.Concat ("\"", s, "\"");
755 StringBuilder sb = new StringBuilder (len + 10);
758 for (int si = 0; si < len; si++) {
761 else if (s [si] == '\\')
768 return sb.ToString ();
769 } else if (ob is bool) {
770 return ob.ToString ().ToLower (Helpers.InvariantCulture);
772 return ob.ToString ();
776 sealed class ScriptEntry
778 public readonly Type Type;
779 public readonly string Key;
780 public readonly string Script;
781 public readonly ScriptEntryFormat Format;
782 public ScriptEntry Next;
784 public ScriptEntry (Type type, string key, string script, ScriptEntryFormat format) {
792 enum ScriptEntryFormat
800 internal static string EnsureEndsWithSemicolon (string value) {
801 if (value != null && value.Length > 0 && value [value.Length - 1] != ';')