2008-03-13 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.UI / ClientScriptManager.cs
1 //
2 // System.Web.UI.ClientScriptManager.cs
3 //
4 // Authors:
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)
9 //
10 // (C) 2002,2003 Ximian, Inc. (http://www.ximian.com)
11 // (c) 2003 Novell, Inc. (http://www.novell.com)
12 //
13
14 //
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:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
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.
33 //
34
35 using System;
36 using System.Collections;
37 #if NET_2_0
38 using System.Collections.Generic;
39 #endif
40 using System.Text;
41 using System.Collections.Specialized;
42 using System.Web.Util;
43 using System.Globalization;
44
45 namespace System.Web.UI
46 {
47         #if NET_2_0
48         public sealed partial
49         #else
50         internal
51         #endif
52         class ClientScriptManager
53         {
54                 internal const string EventStateFieldName = "__EVENTVALIDATION";
55                 
56                 Hashtable registeredArrayDeclares;
57                 ScriptEntry clientScriptBlocks;
58                 ScriptEntry startupScriptBlocks;
59                 internal Hashtable hiddenFields;
60                 ScriptEntry submitStatements;
61                 Page page;
62 #if NET_2_0
63                 int [] eventValidationValues;
64                 int eventValidationPos = 0;
65                 Hashtable expandoAttributes;
66                 bool _hasRegisteredForEventValidationOnCallback;
67                 bool _pageInRender;
68                 bool _initCallBackRegistered;
69 #endif
70                 
71                 internal ClientScriptManager (Page page)
72                 {
73                         this.page = page;
74                 }
75
76 #if !NET_2_0
77                 public string GetPostBackClientEvent (Control control, string argument)
78                 {
79                         return GetPostBackEventReference (control, argument);
80                 }
81 #endif
82
83                 public string GetPostBackClientHyperlink (Control control, string argument)
84                 {
85                         return "javascript:" + GetPostBackEventReference (control, argument);
86                 }
87         
88 #if NET_2_0
89                 public string GetPostBackClientHyperlink (Control control, string argument, bool registerForEventValidation)
90                 {
91                         if (registerForEventValidation)
92                                 RegisterForEventValidation (control.UniqueID, argument);
93                         return "javascript:" + GetPostBackEventReference (control, argument);
94                 }
95 #endif          
96
97 #if !NET_2_0
98                 internal
99 #else
100                 public
101 #endif
102                 string GetPostBackEventReference (Control control, string argument)
103                 {
104                         if (control == null)
105                                 throw new ArgumentNullException ("control");
106                         
107                         page.RequiresPostBackScript ();
108                         if(page.IsMultiForm)
109                                 return page.theForm + ".__doPostBack('" + control.UniqueID + "','" + argument + "')";
110                         else
111                                 return "__doPostBack('" + control.UniqueID + "','" + argument + "')";
112                 }
113
114 #if NET_2_0
115                 public string GetPostBackEventReference (Control control, string argument, bool registerForEventValidation)
116                 {
117                         if (control == null)
118                                 throw new ArgumentNullException ("control");
119                         
120                         if (registerForEventValidation)
121                                 RegisterForEventValidation (control.UniqueID, argument);
122                         return GetPostBackEventReference (control, argument);
123                 }
124                 
125                 public string GetPostBackEventReference (PostBackOptions options, bool registerForEventValidation)
126                 {
127                         if (options == null)
128                                 throw new ArgumentNullException ("options");
129                         if (registerForEventValidation)
130                                 RegisterForEventValidation (options);
131                         return GetPostBackEventReference (options);
132                 }
133                 
134                 public string GetPostBackEventReference (PostBackOptions options)
135                 {
136                         if (options == null)
137                                 throw new ArgumentNullException ("options");
138
139                         if (options.ActionUrl == null && options.ValidationGroup == null && !options.TrackFocus && 
140                                 !options.AutoPostBack && !options.PerformValidation)
141                         {
142                                 if (!options.ClientSubmit)
143                                         return null;
144
145                                 if (options.RequiresJavaScriptProtocol)
146                                         return GetPostBackClientHyperlink (options.TargetControl, options.Argument);
147                                 else
148                                         return GetPostBackEventReference (options.TargetControl, options.Argument);
149                         }
150
151                         RegisterWebFormClientScript ();
152
153                         string actionUrl = options.ActionUrl;
154                         if (actionUrl != null)
155                                 RegisterHiddenField (Page.PreviousPageID, page.Request.FilePath);
156
157                         if(options.TrackFocus)
158                                 RegisterHiddenField (Page.LastFocusID, String.Empty);
159
160                         string prefix = options.RequiresJavaScriptProtocol ? "javascript:" : "";
161                         if (page.IsMultiForm)
162                                 prefix += page.theForm + ".";
163
164                         return prefix + "WebForm_DoPostback(" +
165                                 ClientScriptManager.GetScriptLiteral (options.TargetControl.UniqueID) + "," +
166                                 ClientScriptManager.GetScriptLiteral (options.Argument) + "," +
167                                 ClientScriptManager.GetScriptLiteral (actionUrl) + "," +
168                                 ClientScriptManager.GetScriptLiteral (options.AutoPostBack) + "," +
169                                 ClientScriptManager.GetScriptLiteral (options.PerformValidation) + "," +
170                                 ClientScriptManager.GetScriptLiteral (options.TrackFocus) + "," +
171                                 ClientScriptManager.GetScriptLiteral (options.ClientSubmit) + "," +
172                                 ClientScriptManager.GetScriptLiteral (options.ValidationGroup) + ")";
173                 }
174
175                 internal void RegisterWebFormClientScript ()
176                 {
177                         if (_webFormClientScriptRequired)
178                                 return;
179
180                         page.RequiresPostBackScript ();
181                         _webFormClientScriptRequired = true;
182                 }
183
184                 bool _webFormClientScriptRendered;
185                 bool _webFormClientScriptRequired;
186
187                 internal void WriteWebFormClientScript (HtmlTextWriter writer) {
188                         if (!_webFormClientScriptRendered && _webFormClientScriptRequired) {
189                                 writer.WriteLine ();
190                                 WriteClientScriptInclude (writer, GetWebResourceUrl (typeof (Page), "webform.js"), typeof (Page), "webform.js");
191                                 WriteBeginScriptBlock (writer);
192                                 writer.WriteLine ("WebForm_Initialize({0});", page.IsMultiForm ? page.theForm : "window");
193                                 WriteEndScriptBlock (writer);
194                                 _webFormClientScriptRendered = true;
195                         }
196                 }
197                 
198                 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context)
199                 {
200                         return GetCallbackEventReference (control, argument, clientCallback, context, null, false);
201                 }
202
203                 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, bool useAsync)
204                 {
205                         return GetCallbackEventReference (control, argument, clientCallback, context, null, useAsync);
206                 }
207
208                 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
209                 {
210                         if (control == null)
211                                 throw new ArgumentNullException ("control");
212                         if(!(control is ICallbackEventHandler))
213                                 throw new InvalidOperationException ("The control must implement the ICallbackEventHandler interface and provide a RaiseCallbackEvent method.");
214
215                         return GetCallbackEventReference ("'" + control.UniqueID + "'", argument, clientCallback, context, clientErrorCallback, useAsync);
216                 }
217
218                 public string GetCallbackEventReference (string target, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
219                 {
220                         RegisterWebFormClientScript ();
221
222                         if (!_initCallBackRegistered) {
223                                 _initCallBackRegistered = true;
224                                 RegisterStartupScript (typeof (Page), "WebForm_InitCallback", page.WebFormScriptReference + ".WebForm_InitCallback();", true);
225                         }
226                         return page.WebFormScriptReference + ".WebForm_DoCallback(" +
227                                 target + "," +
228                                 (argument ?? "null") + "," +
229                                 clientCallback + "," +
230                                 (context ?? "null") + "," +
231                                 (clientErrorCallback ?? "null") + "," +
232                                 (useAsync ? "true" : "false") + ")";
233                 }
234 #endif
235                 
236 #if NET_2_0
237                 public
238 #else
239                 internal
240 #endif
241                 string GetWebResourceUrl(Type type, string resourceName)
242                 {
243                         if (type == null)
244                                 throw new ArgumentNullException ("type");
245                 
246                         if (resourceName == null || resourceName.Length == 0)
247                                 throw new ArgumentNullException ("type");
248                 
249                         return System.Web.Handlers.AssemblyResourceLoader.GetResourceUrl (type, resourceName); 
250                 }
251                 
252
253                 public bool IsClientScriptBlockRegistered (string key)
254                 {
255                         return IsScriptRegistered (clientScriptBlocks, GetType(), key);
256                 }
257         
258                 public bool IsClientScriptBlockRegistered (Type type, string key)
259                 {
260                         return IsScriptRegistered (clientScriptBlocks, type, key);
261                 }
262         
263                 public bool IsStartupScriptRegistered (string key)
264                 {
265                         return IsScriptRegistered (startupScriptBlocks, GetType(), key);
266                 }
267         
268                 public bool IsStartupScriptRegistered (Type type, string key)
269                 {
270                         return IsScriptRegistered (startupScriptBlocks, type, key);
271                 }
272                 
273                 public bool IsOnSubmitStatementRegistered (string key)
274                 {
275                         return IsScriptRegistered (submitStatements, GetType(), key);
276                 }
277         
278                 public bool IsOnSubmitStatementRegistered (Type type, string key)
279                 {
280                         return IsScriptRegistered (submitStatements, type, key);
281                 }
282                 
283                 public bool IsClientScriptIncludeRegistered (string key)
284                 {
285                         return IsClientScriptIncludeRegistered (GetType (), key);
286                 }
287         
288                 public bool IsClientScriptIncludeRegistered (Type type, string key)
289                 {
290                         return IsScriptRegistered (clientScriptBlocks, type, "include-" + key);
291                 }
292                 
293                 bool IsScriptRegistered (ScriptEntry scriptList, Type type, string key)
294                 {
295                         while (scriptList != null) {
296                                 if (scriptList.Type == type && scriptList.Key == key)
297                                         return true;
298                                 scriptList = scriptList.Next;
299                         }
300                         return false;
301                 }
302                 
303                 public void RegisterArrayDeclaration (string arrayName, string arrayValue)
304                 {
305                         if (registeredArrayDeclares == null)
306                                 registeredArrayDeclares = new Hashtable();
307         
308                         if (!registeredArrayDeclares.ContainsKey (arrayName))
309                                 registeredArrayDeclares.Add (arrayName, new ArrayList());
310         
311                         ((ArrayList) registeredArrayDeclares[arrayName]).Add(arrayValue);
312                 }
313
314                 void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, bool addScriptTags)
315                 {
316                         RegisterScript (ref scriptList, type, key, script, addScriptTags ? ScriptEntryFormat.AddScriptTag : ScriptEntryFormat.None);
317                 }
318
319                 void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, ScriptEntryFormat format)
320                 {
321                         ScriptEntry last = null;
322                         ScriptEntry entry = scriptList;
323
324                         while (entry != null) {
325                                 if (entry.Type == type && entry.Key == key)
326                                         return;
327                                 last = entry;
328                                 entry = entry.Next;
329                         }
330
331                         entry = new ScriptEntry (type, key, script, format);
332                         
333                         if (last != null) last.Next = entry;
334                         else scriptList = entry;
335                 }
336         
337                 internal void RegisterClientScriptBlock (string key, string script)
338                 {
339                         RegisterScript (ref clientScriptBlocks, GetType(), key, script, false);
340                 }
341         
342                 public void RegisterClientScriptBlock (Type type, string key, string script)
343                 {
344                         RegisterClientScriptBlock (type, key, script, false);
345                 }
346         
347                 public void RegisterClientScriptBlock (Type type, string key, string script, bool addScriptTags)
348                 {
349                         if (type == null)
350                                 throw new ArgumentNullException ("type");
351
352                         RegisterScript (ref clientScriptBlocks, type, key, script, addScriptTags);
353                 }
354         
355                 public void RegisterHiddenField (string hiddenFieldName, string hiddenFieldInitialValue)
356                 {
357                         if (hiddenFields == null)
358                                 hiddenFields = new Hashtable ();
359
360                         if (!hiddenFields.ContainsKey (hiddenFieldName))
361                                 hiddenFields.Add (hiddenFieldName, hiddenFieldInitialValue);
362                 }
363         
364                 internal void RegisterOnSubmitStatement (string key, string script)
365                 {
366                         RegisterScript (ref submitStatements, GetType (), key, script, false);
367                 }
368         
369                 public void RegisterOnSubmitStatement (Type type, string key, string script)
370                 {
371                         if (type == null)
372                                 throw new ArgumentNullException ("type");
373                         
374                         RegisterScript (ref submitStatements, type, key, script, false);
375                 }
376         
377                 internal void RegisterStartupScript (string key, string script)
378                 {
379                         RegisterScript (ref startupScriptBlocks, GetType(), key, script, false);
380                 }
381                 
382                 public void RegisterStartupScript (Type type, string key, string script)
383                 {
384                         RegisterStartupScript (type, key, script, false);
385                 }
386                 
387                 public void RegisterStartupScript (Type type, string key, string script, bool addScriptTags)
388                 {
389                         if (type == null)
390                                 throw new ArgumentNullException ("type");
391
392                         RegisterScript (ref startupScriptBlocks, type, key, script, addScriptTags);
393                 }
394
395                 public void RegisterClientScriptInclude (string key, string url)
396                 {
397                         RegisterClientScriptInclude (GetType (), key, url);
398                 }
399                 
400                 public void RegisterClientScriptInclude (Type type, string key, string url)
401                 {
402                         if (type == null)
403                                 throw new ArgumentNullException ("type");
404                         if (url == null || url.Length == 0)
405                                 throw new ArgumentException ("url");
406
407                         RegisterScript (ref clientScriptBlocks, type, "include-" + key, url, ScriptEntryFormat.Include);
408                 }
409
410 #if NET_2_0
411                 public void RegisterClientScriptResource (Type type, string resourceName)
412                 {
413                         RegisterScript (ref clientScriptBlocks, type, "resource-" + resourceName, GetWebResourceUrl (type, resourceName), ScriptEntryFormat.Include);
414                 }
415
416                 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue)
417                 {
418                         RegisterExpandoAttribute (controlId, attributeName, attributeValue, true);
419                 }
420
421                 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue, bool encode)
422                 {
423                         if (controlId == null)
424                                 throw new ArgumentNullException ("controlId");
425
426                         if (attributeName == null)
427                                 throw new ArgumentNullException ("attributeName");
428                         
429                         if (expandoAttributes == null)
430                                 expandoAttributes = new Hashtable ();
431
432                         ListDictionary list = (ListDictionary)expandoAttributes [controlId];
433                         if (list == null) {
434                                 list = new ListDictionary ();
435                                 expandoAttributes [controlId] = list;
436                         }
437
438                         list.Add (attributeName, encode ? StrUtils.EscapeQuotesAndBackslashes (attributeValue) : attributeValue);
439                 }
440
441                 private void EnsureEventValidationArray ()
442                 {
443                         if (eventValidationValues == null || eventValidationValues.Length == 0)
444                                 eventValidationValues = new int [64];
445
446                         int len = eventValidationValues.Length;
447
448                         if (eventValidationPos >= len) {
449                                 int [] tmp = new int [len * 2];
450                                 Array.Copy (eventValidationValues, tmp, len);
451                                 eventValidationValues = tmp;
452                         }
453                 }
454
455                 internal void ResetEventValidationState ()
456                 {
457                         _pageInRender = true;
458                         eventValidationPos = 0;
459                 }
460
461                 // Implemented following the description in http://odetocode.com/Blogs/scott/archive/2006/03/20/3145.aspx
462                 private int CalculateEventHash (string uniqueId, string argument)
463                 {
464                         int uniqueIdHash = uniqueId.GetHashCode ();
465                         int argumentHash = String.IsNullOrEmpty (argument) ? 0 : argument.GetHashCode ();
466                         return (uniqueIdHash ^ argumentHash);
467                 }
468                 
469                 public void RegisterForEventValidation (PostBackOptions options)
470                 {
471                         // MS.NET does not check for options == null, so we won't too...
472                         RegisterForEventValidation (options.TargetControl.UniqueID, options.Argument);
473                 }
474                 
475                 public void RegisterForEventValidation (string uniqueId)
476                 {
477                         RegisterForEventValidation (uniqueId, null);
478                 }
479                 
480                 public void RegisterForEventValidation (string uniqueId, string argument)
481                 {
482                         if (!page.EnableEventValidation)
483                                 return;
484                         if (uniqueId == null || uniqueId.Length == 0)
485                                 return;
486                         if (page.IsCallback)
487                                 _hasRegisteredForEventValidationOnCallback = true;
488                         else if (!_pageInRender)
489                                 throw new InvalidOperationException ("RegisterForEventValidation may only be called from the Render method");
490
491                         EnsureEventValidationArray ();
492                         
493                         int hash = CalculateEventHash (uniqueId, argument);
494                         for (int i = 0; i < eventValidationPos; i++)
495                                 if (eventValidationValues [i] == hash)
496                                         return;
497                         eventValidationValues [eventValidationPos++] = hash;
498                 }
499
500                 public void ValidateEvent (string uniqueId)
501                 {
502                         ValidateEvent (uniqueId, null);
503                 }
504
505                 public void ValidateEvent (string uniqueId, string argument)
506                 {
507                         if (uniqueId == null || uniqueId.Length == 0)
508                                 throw new ArgumentException ("must not be null or empty", "uniqueId");
509                         if (!page.EnableEventValidation)
510                                 return;
511                         if (eventValidationValues == null)
512                                 goto bad;
513                         
514                         int hash = CalculateEventHash (uniqueId, argument);
515                         for (int i = 0; i < eventValidationValues.Length; i++)
516                                 if (eventValidationValues [i] == hash)
517                                         return;
518                         
519                         bad:
520                         throw 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.");
521                 }
522 #endif
523                 void WriteScripts (HtmlTextWriter writer, ScriptEntry scriptList)
524                 {
525                         if (scriptList == null)
526                                 return;
527
528                         writer.WriteLine ();
529
530                         while (scriptList != null) {
531                                 switch (scriptList.Format) {
532                                 case ScriptEntryFormat.AddScriptTag:
533                                         EnsureBeginScriptBlock (writer);
534                                         writer.Write (scriptList.Script);
535                                         break;
536                                 case ScriptEntryFormat.Include:
537                                         EnsureEndScriptBlock (writer);
538                                         WriteClientScriptInclude (writer, scriptList.Script, scriptList.Type, scriptList.Key);
539                                         break;
540                                 default:
541                                         EnsureEndScriptBlock (writer);
542                                         writer.WriteLine (scriptList.Script);
543                                         break;
544                                 }
545                                 scriptList = scriptList.Next;
546                         }
547                         EnsureEndScriptBlock (writer);
548                 }
549
550                 bool _scriptTagOpened;
551
552                 void EnsureBeginScriptBlock (HtmlTextWriter writer) {
553                         if (!_scriptTagOpened) {
554                                 WriteBeginScriptBlock (writer);
555                                 _scriptTagOpened = true;
556                         }
557                 }
558
559                 void EnsureEndScriptBlock (HtmlTextWriter writer) {
560                         if (_scriptTagOpened) {
561                                 WriteEndScriptBlock (writer);
562                                 _scriptTagOpened = false;
563                         }
564                 }
565
566 #if NET_2_0
567                 internal void RestoreEventValidationState (string fieldValue)
568                 {
569                         if (!page.EnableEventValidation || fieldValue == null || fieldValue.Length == 0)
570                                 return;
571                         IStateFormatter fmt = page.GetFormatter ();
572                         eventValidationValues = (int []) fmt.Deserialize (fieldValue);
573                         eventValidationPos = eventValidationValues.Length;
574                 }
575                 
576                 internal void SaveEventValidationState ()
577                 {
578                         if (!page.EnableEventValidation)
579                                 return;
580
581                         string eventValidation = GetEventValidationStateFormatted ();
582                         if (eventValidation == null)
583                                 return;
584
585                         RegisterHiddenField (EventStateFieldName, eventValidation);
586                 }
587
588                 internal string GetEventValidationStateFormatted ()
589                 {
590                         if (eventValidationValues == null || eventValidationValues.Length == 0)
591                                 return null;
592
593                         if(page.IsCallback && !_hasRegisteredForEventValidationOnCallback)
594                                 return null;
595
596                         IStateFormatter fmt = page.GetFormatter ();
597                         int [] array = new int [eventValidationPos];
598                         Array.Copy (eventValidationValues, array, eventValidationPos);
599                         return fmt.Serialize (array);
600                 }
601
602                 internal void WriteExpandoAttributes (HtmlTextWriter writer)
603                 {
604                         if (expandoAttributes == null)
605                                 return;
606
607                         writer.WriteLine ();
608                         WriteBeginScriptBlock (writer);
609
610                         foreach (string controlId in expandoAttributes.Keys) {
611                                 writer.WriteLine ("var {0} = document.all ? document.all [\"{0}\"] : document.getElementById (\"{0}\");", controlId);
612                                 ListDictionary attrs = (ListDictionary) expandoAttributes [controlId];
613                                 foreach (string attributeName in attrs.Keys) {
614                                         writer.WriteLine ("{0}.{1} = \"{2}\";", controlId, attributeName, attrs [attributeName]);
615                                 }
616                         }
617                         WriteEndScriptBlock (writer);
618                         writer.WriteLine ();
619                 }
620
621 #endif
622                 
623 #if NET_2_0
624                 internal const string SCRIPT_BLOCK_START = "//<![CDATA[";
625                 internal const string SCRIPT_BLOCK_END = "//]]>";
626 #else
627                 internal const string SCRIPT_BLOCK_START = "<!--";
628                 internal const string SCRIPT_BLOCK_END ="// -->";
629 #endif
630                 
631                 internal static void WriteBeginScriptBlock (HtmlTextWriter writer)
632                 {
633                         writer.WriteLine ("<script"+
634 #if !NET_2_0
635                                 " language=\"javascript\""+
636 #endif
637                                 " type=\"text/javascript\">");
638                         writer.WriteLine (SCRIPT_BLOCK_START);
639                 }
640
641                 internal static void WriteEndScriptBlock (HtmlTextWriter writer)
642                 {
643                         writer.WriteLine (SCRIPT_BLOCK_END);
644                         writer.WriteLine ("</script>");
645                 }
646                 
647                 internal void WriteHiddenFields (HtmlTextWriter writer)
648                 {
649                         if (hiddenFields == null)
650                                 return;
651
652 #if NET_2_0
653                         writer.RenderBeginTag (HtmlTextWriterTag.Div);
654 #endif
655                         foreach (string key in hiddenFields.Keys) {
656                                 string value = hiddenFields [key] as string;
657                                 writer.WriteLine ("<input type=\"hidden\" name=\"{0}\" id=\"{0}\" value=\"{1}\" />", key, HttpUtility.HtmlAttributeEncode (value));
658                         }
659 #if NET_2_0
660                         writer.RenderEndTag (); // DIV
661 #endif
662                         hiddenFields = null;
663                 }
664                 
665                 internal void WriteClientScriptInclude (HtmlTextWriter writer, string path, Type type, string key) {
666                                         if (!page.IsMultiForm)
667                                                 writer.WriteLine ("<script src=\"{0}\" type=\"text/javascript\"></script>", path);
668                                         else {
669                                                 string scriptKey = "inc_" + (type.FullName + key).GetHashCode ().ToString ("X");
670                                                 writer.WriteLine ("<script type=\"text/javascript\">");
671                                                 writer.WriteLine (SCRIPT_BLOCK_START);
672                                                 writer.WriteLine ("if (document.{0} == null) {{", scriptKey);
673                                                 writer.WriteLine ("\tdocument.{0} = true", scriptKey);
674                                                 writer.WriteLine ("\tdocument.write('<script src=\"{0}\" type=\"text/javascript\"><\\/script>'); }}", path);
675                                                 writer.WriteLine (SCRIPT_BLOCK_END);
676                                                 writer.WriteLine ("</script>");
677                                         }
678                 }
679                 
680                 internal void WriteClientScriptBlocks (HtmlTextWriter writer)
681                 {
682                         WriteScripts (writer, clientScriptBlocks);
683                 }
684         
685                 internal void WriteStartupScriptBlocks (HtmlTextWriter writer)
686                 {
687                         WriteScripts (writer, startupScriptBlocks);
688                 }
689         
690                 internal void WriteArrayDeclares (HtmlTextWriter writer)
691                 {
692                         if (registeredArrayDeclares != null) {
693                                 writer.WriteLine();
694                                 WriteBeginScriptBlock (writer);
695                                 IDictionaryEnumerator arrayEnum = registeredArrayDeclares.GetEnumerator();
696                                 while (arrayEnum.MoveNext()) {
697                                         if (page.IsMultiForm)
698                                                 writer.Write ("\t" + page.theForm + ".");
699                                         else
700                                                 writer.Write ("\tvar ");
701                                         writer.Write(arrayEnum.Key);
702                                         writer.Write(" =  new Array(");
703                                         IEnumerator arrayListEnum = ((ArrayList) arrayEnum.Value).GetEnumerator();
704                                         bool isFirst = true;
705                                         while (arrayListEnum.MoveNext()) {
706                                                 if (isFirst)
707                                                         isFirst = false;
708                                                 else
709                                                         writer.Write(", ");
710                                                 writer.Write(arrayListEnum.Current);
711                                         }
712                                         writer.WriteLine(");");
713                                 }
714                                 WriteEndScriptBlock (writer);
715                                 writer.WriteLine ();
716                         }
717                 }
718
719 #if NET_2_0
720                 internal string GetClientValidationEvent (string validationGroup) {
721                         if (page.IsMultiForm)
722                                 return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate('" + validationGroup + "');";
723                         return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate('" + validationGroup + "');";
724                 }
725 #endif
726
727                 internal string GetClientValidationEvent ()
728                 {
729                         if (page.IsMultiForm)
730                                 return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate();";
731                         return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate();";
732                 }
733
734
735                 internal string WriteSubmitStatements ()
736                 {
737                         if (submitStatements == null) return null;
738                         
739                         StringBuilder sb = new StringBuilder ();
740                         ScriptEntry entry = submitStatements;
741                         while (entry != null) {
742 #if NET_2_0
743                                 sb.Append (EnsureEndsWithSemicolon (entry.Script));
744 #else
745                                 sb.Append (entry.Script);
746 #endif
747                                 entry = entry.Next;
748                         }
749 #if NET_2_0
750                         RegisterClientScriptBlock (GetType(), "HtmlForm-OnSubmitStatemen",
751 @"
752 " + page.WebFormScriptReference + @".WebForm_OnSubmit = function () {
753 " + sb.ToString () + @"
754 return true;
755 }
756 ", true);
757                         return "javascript:return " + page.WebFormScriptReference + ".WebForm_OnSubmit();";
758
759 #else
760                         return sb.ToString ();
761 #endif
762                 }
763                 
764                 internal static string GetScriptLiteral (object ob)
765                 {
766                         if (ob == null)
767                                 return "null";
768                         else if (ob is string) {
769                                 string s = (string)ob;
770                                 bool escape = false;
771                                 int len = s.Length;
772
773                                 for (int i = 0; i < len; i++)
774                                         if (s [i] == '\\' || s [i] == '\"') {
775                                                 escape = true;
776                                                 break;
777                                         }
778
779                                 if (!escape)
780                                         return string.Concat ("\"", s, "\"");
781
782                                 StringBuilder sb = new StringBuilder (len + 10);
783
784                                 sb.Append ('\"');
785                                 for (int si = 0; si < len; si++) {
786                                         if (s [si] == '\"')
787                                                 sb.Append ("\\\"");
788                                         else if (s [si] == '\\')
789                                                 sb.Append ("\\\\");
790                                         else
791                                                 sb.Append (s [si]);
792                                 }
793                                 sb.Append ('\"');
794
795                                 return sb.ToString ();
796                         } else if (ob is bool) {
797                                 return ob.ToString ().ToLower (CultureInfo.InvariantCulture);
798                         } else {
799                                 return ob.ToString ();
800                         }
801                 }
802
803                 sealed class ScriptEntry
804                 {
805                         public readonly Type Type;
806                         public readonly string Key;
807                         public readonly string Script;
808                         public readonly ScriptEntryFormat Format;
809                         public ScriptEntry Next;
810
811                         public ScriptEntry (Type type, string key, string script, ScriptEntryFormat format) {
812                                 Key = key;
813                                 Type = type;
814                                 Script = script;
815                                 Format = format;
816                         }
817                 }
818
819                 enum ScriptEntryFormat
820                 {
821                         None,
822                         AddScriptTag,
823                         Include,
824                 }
825
826 #if NET_2_0
827                 // helper method
828                 internal static string EnsureEndsWithSemicolon (string value) {
829                         if (value != null && value.Length > 0 && value [value.Length - 1] != ';')
830                                 return value += ";";
831                         return value;
832                 }
833 #endif
834         }
835 }