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