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