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