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