Add a more functional (i.e. fewer-stubs) implementation of System.Data.Linq.
[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                 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                 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                 ArgumentException InvalidPostBackException ()
506                 {
507                         return new ArgumentException ("Invalid postback or callback argument. Event validation is enabled using <pages enableEventValidation=\"true\"/> in configuration or <%@ Page EnableEventValidation=\"true\" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.");
508                 }
509                 
510                 public void ValidateEvent (string uniqueId, string argument)
511                 {
512                         if (uniqueId == null || uniqueId.Length == 0)
513                                 throw new ArgumentException ("must not be null or empty", "uniqueId");
514                         if (!page.EnableEventValidation)
515                                 return;
516                         if (eventValidationValues == null)
517                                 throw InvalidPostBackException ();
518                         
519                         int hash = CalculateEventHash (uniqueId, argument);
520                         for (int i = 0; i < eventValidationValues.Length; i++)
521                                 if (eventValidationValues [i] == hash)
522                                         return;
523                         
524                         throw InvalidPostBackException ();
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 (!window.{0}) {{", scriptKey);
677                                                 writer.WriteLine ("\twindow.{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.WebFormScriptReference + @".WebForm_OnSubmit = function () {
757 " + sb.ToString () + @"
758 return true;
759 }
760 ", true);
761                         return "javascript:return " + page.WebFormScriptReference + ".WebForm_OnSubmit();";
762
763 #else
764                         return sb.ToString ();
765 #endif
766                 }
767                 
768                 internal static string GetScriptLiteral (object ob)
769                 {
770                         if (ob == null)
771                                 return "null";
772                         else if (ob is string) {
773                                 string s = (string)ob;
774                                 bool escape = false;
775                                 int len = s.Length;
776
777                                 for (int i = 0; i < len; i++)
778                                         if (s [i] == '\\' || s [i] == '\"') {
779                                                 escape = true;
780                                                 break;
781                                         }
782
783                                 if (!escape)
784                                         return string.Concat ("\"", s, "\"");
785
786                                 StringBuilder sb = new StringBuilder (len + 10);
787
788                                 sb.Append ('\"');
789                                 for (int si = 0; si < len; si++) {
790                                         if (s [si] == '\"')
791                                                 sb.Append ("\\\"");
792                                         else if (s [si] == '\\')
793                                                 sb.Append ("\\\\");
794                                         else
795                                                 sb.Append (s [si]);
796                                 }
797                                 sb.Append ('\"');
798
799                                 return sb.ToString ();
800                         } else if (ob is bool) {
801                                 return ob.ToString ().ToLower (CultureInfo.InvariantCulture);
802                         } else {
803                                 return ob.ToString ();
804                         }
805                 }
806
807                 sealed class ScriptEntry
808                 {
809                         public readonly Type Type;
810                         public readonly string Key;
811                         public readonly string Script;
812                         public readonly ScriptEntryFormat Format;
813                         public ScriptEntry Next;
814
815                         public ScriptEntry (Type type, string key, string script, ScriptEntryFormat format) {
816                                 Key = key;
817                                 Type = type;
818                                 Script = script;
819                                 Format = format;
820                         }
821                 }
822
823                 enum ScriptEntryFormat
824                 {
825                         None,
826                         AddScriptTag,
827                         Include,
828                 }
829
830 #if NET_2_0
831                 // helper method
832                 internal static string EnsureEndsWithSemicolon (string value) {
833                         if (value != null && value.Length > 0 && value [value.Length - 1] != ';')
834                                 return value += ";";
835                         return value;
836                 }
837 #endif
838         }
839 }