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