Merge branch 'master' of git://github.com/mono/mono
[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 #if NET_4_0
628                         writer.AddAttribute (HtmlTextWriterAttribute.Class, "aspNetHidden");
629 #endif
630                         writer.RenderBeginTag (HtmlTextWriterTag.Div);
631                         int oldIndent = writer.Indent;
632                         writer.Indent = 0;
633                         bool first = true;
634
635                         foreach (string key in hiddenFields.Keys) {
636                                 string value = hiddenFields [key] as string;
637                                 if (first)
638                                         first = false;
639                                 else
640                                         writer.WriteLine ();
641                                 writer.Write ("<input type=\"hidden\" name=\"{0}\" id=\"{0}\" value=\"{1}\" />", key, HttpUtility.HtmlAttributeEncode (value));
642                         }
643                         writer.Indent = oldIndent;
644                         writer.RenderEndTag (); // DIV
645                         writer.WriteLine ();
646                         hiddenFields = null;
647                 }
648                 
649                 internal void WriteClientScriptInclude (HtmlTextWriter writer, string path, Type type, string key) {
650                                         if (!page.IsMultiForm)
651                                                 writer.WriteLine ("<script src=\"{0}\" type=\"text/javascript\"></script>", path);
652                                         else {
653                                                 string scriptKey = "inc_" + (type.FullName + key).GetHashCode ().ToString ("X");
654                                                 writer.WriteLine ("<script type=\"text/javascript\">");
655                                                 writer.WriteLine (SCRIPT_BLOCK_START);
656                                                 writer.WriteLine ("if (!window.{0}) {{", scriptKey);
657                                                 writer.WriteLine ("\twindow.{0} = true", scriptKey);
658                                                 writer.WriteLine ("\tdocument.write('<script src=\"{0}\" type=\"text/javascript\"><\\/script>'); }}", path);
659                                                 writer.WriteLine (SCRIPT_BLOCK_END);
660                                                 writer.WriteLine ("</script>");
661                                         }
662                 }
663                 
664                 internal void WriteClientScriptBlocks (HtmlTextWriter writer)
665                 {
666                         WriteScripts (writer, clientScriptBlocks);
667                 }
668         
669                 internal void WriteStartupScriptBlocks (HtmlTextWriter writer)
670                 {
671                         WriteScripts (writer, startupScriptBlocks);
672                 }
673         
674                 internal void WriteArrayDeclares (HtmlTextWriter writer)
675                 {
676                         if (registeredArrayDeclares != null) {
677                                 writer.WriteLine();
678                                 WriteBeginScriptBlock (writer);
679                                 IDictionaryEnumerator arrayEnum = registeredArrayDeclares.GetEnumerator();
680                                 while (arrayEnum.MoveNext()) {
681                                         if (page.IsMultiForm)
682                                                 writer.Write ("\t" + page.theForm + ".");
683                                         else
684                                                 writer.Write ("\tvar ");
685                                         writer.Write(arrayEnum.Key);
686                                         writer.Write(" =  new Array(");
687                                         IEnumerator arrayListEnum = ((ArrayList) arrayEnum.Value).GetEnumerator();
688                                         bool isFirst = true;
689                                         while (arrayListEnum.MoveNext()) {
690                                                 if (isFirst)
691                                                         isFirst = false;
692                                                 else
693                                                         writer.Write(", ");
694                                                 writer.Write(arrayListEnum.Current);
695                                         }
696                                         writer.WriteLine(");");
697                                 }
698                                 WriteEndScriptBlock (writer);
699                                 writer.WriteLine ();
700                         }
701                 }
702
703                 internal string GetClientValidationEvent (string validationGroup) {
704                         if (page.IsMultiForm)
705                                 return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate('" + validationGroup + "');";
706                         return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate('" + validationGroup + "');";
707                 }
708
709                 internal string GetClientValidationEvent ()
710                 {
711                         if (page.IsMultiForm)
712                                 return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate();";
713                         return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate();";
714                 }
715
716
717                 internal string WriteSubmitStatements ()
718                 {
719                         if (submitStatements == null) return null;
720                         
721                         StringBuilder sb = new StringBuilder ();
722                         ScriptEntry entry = submitStatements;
723                         while (entry != null) {
724                                 sb.Append (EnsureEndsWithSemicolon (entry.Script));
725                                 entry = entry.Next;
726                         }
727                         RegisterClientScriptBlock (GetType(), "HtmlForm-OnSubmitStatemen",
728 @"
729 " + page.WebFormScriptReference + @".WebForm_OnSubmit = function () {
730 " + sb.ToString () + @"
731 return true;
732 }
733 ", true);
734                         return "javascript:return " + page.WebFormScriptReference + ".WebForm_OnSubmit();";
735                 }
736                 
737                 internal static string GetScriptLiteral (object ob)
738                 {
739                         if (ob == null)
740                                 return "null";
741                         else if (ob is string) {
742                                 string s = (string)ob;
743                                 bool escape = false;
744                                 int len = s.Length;
745
746                                 for (int i = 0; i < len; i++)
747                                         if (s [i] == '\\' || s [i] == '\"') {
748                                                 escape = true;
749                                                 break;
750                                         }
751
752                                 if (!escape)
753                                         return string.Concat ("\"", s, "\"");
754
755                                 StringBuilder sb = new StringBuilder (len + 10);
756
757                                 sb.Append ('\"');
758                                 for (int si = 0; si < len; si++) {
759                                         if (s [si] == '\"')
760                                                 sb.Append ("\\\"");
761                                         else if (s [si] == '\\')
762                                                 sb.Append ("\\\\");
763                                         else
764                                                 sb.Append (s [si]);
765                                 }
766                                 sb.Append ('\"');
767
768                                 return sb.ToString ();
769                         } else if (ob is bool) {
770                                 return ob.ToString ().ToLower (Helpers.InvariantCulture);
771                         } else {
772                                 return ob.ToString ();
773                         }
774                 }
775
776                 sealed class ScriptEntry
777                 {
778                         public readonly Type Type;
779                         public readonly string Key;
780                         public readonly string Script;
781                         public readonly ScriptEntryFormat Format;
782                         public ScriptEntry Next;
783
784                         public ScriptEntry (Type type, string key, string script, ScriptEntryFormat format) {
785                                 Key = key;
786                                 Type = type;
787                                 Script = script;
788                                 Format = format;
789                         }
790                 }
791
792                 enum ScriptEntryFormat
793                 {
794                         None,
795                         AddScriptTag,
796                         Include,
797                 }
798
799                 // helper method
800                 internal static string EnsureEndsWithSemicolon (string value) {
801                         if (value != null && value.Length > 0 && value [value.Length - 1] != ';')
802                                 return value += ";";
803                         return value;
804                 }
805         }
806 }