2010-01-20 Zoltan Varga <vargaz@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 Novell, Inc. (http://www.novell.com)
12 //
13
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System;
36 using System.Collections;
37 #if NET_2_0
38 using System.Collections.Generic;
39 #endif
40 using System.Text;
41 using System.Collections.Specialized;
42 using System.Web.Util;
43 using System.Globalization;
44
45 namespace System.Web.UI
46 {
47         #if NET_2_0
48         public sealed partial
49         #else
50         internal
51         #endif
52         class ClientScriptManager
53         {
54                 internal const string EventStateFieldName = "__EVENTVALIDATION";
55                 
56                 Hashtable registeredArrayDeclares;
57                 ScriptEntry clientScriptBlocks;
58                 ScriptEntry startupScriptBlocks;
59                 internal Hashtable hiddenFields;
60                 ScriptEntry submitStatements;
61                 Page page;
62 #if NET_2_0
63                 int [] eventValidationValues;
64                 int eventValidationPos = 0;
65                 Hashtable expandoAttributes;
66                 bool _hasRegisteredForEventValidationOnCallback;
67                 bool _pageInRender;
68                 bool _initCallBackRegistered;
69                 bool _webFormClientScriptRendered;
70                 bool _webFormClientScriptRequired;
71                 
72                 internal bool ScriptsPresent {
73                         get {
74                                 return _webFormClientScriptRequired ||
75                                         _initCallBackRegistered ||
76                                         _hasRegisteredForEventValidationOnCallback ||
77                                         clientScriptBlocks != null ||
78                                         startupScriptBlocks != null ||
79                                         submitStatements != null ||
80                                         registeredArrayDeclares != null ||
81                                         expandoAttributes != null;
82                         }
83                 }
84 #endif
85                 
86                 internal ClientScriptManager (Page page)
87                 {
88                         this.page = page;
89                 }
90
91 #if !NET_2_0
92                 public string GetPostBackClientEvent (Control control, string argument)
93                 {
94                         return GetPostBackEventReference (control, argument);
95                 }
96 #endif
97
98                 public string GetPostBackClientHyperlink (Control control, string argument)
99                 {
100                         return "javascript:" + GetPostBackEventReference (control, argument);
101                 }
102         
103 #if NET_2_0
104                 public string GetPostBackClientHyperlink (Control control, string argument, bool registerForEventValidation)
105                 {
106                         if (registerForEventValidation)
107                                 RegisterForEventValidation (control.UniqueID, argument);
108                         return "javascript:" + GetPostBackEventReference (control, argument);
109                 }
110 #endif          
111
112 #if !NET_2_0
113                 internal
114 #else
115                 public
116 #endif
117                 string GetPostBackEventReference (Control control, string argument)
118                 {
119                         if (control == null)
120                                 throw new ArgumentNullException ("control");
121                         
122                         page.RequiresPostBackScript ();
123                         if(page.IsMultiForm)
124                                 return page.theForm + ".__doPostBack('" + control.UniqueID + "','" + argument + "')";
125                         else
126                                 return "__doPostBack('" + control.UniqueID + "','" + argument + "')";
127                 }
128
129 #if NET_2_0
130                 public string GetPostBackEventReference (Control control, string argument, bool registerForEventValidation)
131                 {
132                         if (control == null)
133                                 throw new ArgumentNullException ("control");
134                         
135                         if (registerForEventValidation)
136                                 RegisterForEventValidation (control.UniqueID, argument);
137                         return GetPostBackEventReference (control, argument);
138                 }
139                 
140                 public string GetPostBackEventReference (PostBackOptions options, bool registerForEventValidation)
141                 {
142                         if (options == null)
143                                 throw new ArgumentNullException ("options");
144                         if (registerForEventValidation)
145                                 RegisterForEventValidation (options);
146                         return GetPostBackEventReference (options);
147                 }
148                 
149                 public string GetPostBackEventReference (PostBackOptions options)
150                 {
151                         if (options == null)
152                                 throw new ArgumentNullException ("options");
153
154                         if (options.ActionUrl == null && options.ValidationGroup == null && !options.TrackFocus && 
155                                 !options.AutoPostBack && !options.PerformValidation)
156                         {
157                                 if (!options.ClientSubmit)
158                                         return null;
159
160                                 if (options.RequiresJavaScriptProtocol)
161                                         return GetPostBackClientHyperlink (options.TargetControl, options.Argument);
162                                 else
163                                         return GetPostBackEventReference (options.TargetControl, options.Argument);
164                         }
165
166                         RegisterWebFormClientScript ();
167
168                         string actionUrl = options.ActionUrl;
169                         if (actionUrl != null)
170                                 RegisterHiddenField (Page.PreviousPageID, page.Request.FilePath);
171
172                         if(options.TrackFocus)
173                                 RegisterHiddenField (Page.LastFocusID, String.Empty);
174
175                         string prefix = options.RequiresJavaScriptProtocol ? "javascript:" : "";
176                         if (page.IsMultiForm)
177                                 prefix += page.theForm + ".";
178
179                         return prefix + "WebForm_DoPostback(" +
180                                 ClientScriptManager.GetScriptLiteral (options.TargetControl.UniqueID) + "," +
181                                 ClientScriptManager.GetScriptLiteral (options.Argument) + "," +
182                                 ClientScriptManager.GetScriptLiteral (actionUrl) + "," +
183                                 ClientScriptManager.GetScriptLiteral (options.AutoPostBack) + "," +
184                                 ClientScriptManager.GetScriptLiteral (options.PerformValidation) + "," +
185                                 ClientScriptManager.GetScriptLiteral (options.TrackFocus) + "," +
186                                 ClientScriptManager.GetScriptLiteral (options.ClientSubmit) + "," +
187                                 ClientScriptManager.GetScriptLiteral (options.ValidationGroup) + ")";
188                 }
189
190                 internal void RegisterWebFormClientScript ()
191                 {
192                         if (_webFormClientScriptRequired)
193                                 return;
194
195                         page.RequiresPostBackScript ();
196                         _webFormClientScriptRequired = true;
197                 }
198
199                 internal void WriteWebFormClientScript (HtmlTextWriter writer) {
200                         if (!_webFormClientScriptRendered && _webFormClientScriptRequired) {
201                                 writer.WriteLine ();
202                                 WriteClientScriptInclude (writer, GetWebResourceUrl (typeof (Page), "webform.js"), typeof (Page), "webform.js");
203                                 WriteBeginScriptBlock (writer);
204                                 writer.WriteLine ("WebForm_Initialize({0});", page.IsMultiForm ? page.theForm : "window");
205                                 WriteEndScriptBlock (writer);
206                                 _webFormClientScriptRendered = true;
207                         }
208                 }
209                 
210                 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context)
211                 {
212                         return GetCallbackEventReference (control, argument, clientCallback, context, null, false);
213                 }
214
215                 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, bool useAsync)
216                 {
217                         return GetCallbackEventReference (control, argument, clientCallback, context, null, useAsync);
218                 }
219
220                 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
221                 {
222                         if (control == null)
223                                 throw new ArgumentNullException ("control");
224                         if(!(control is ICallbackEventHandler))
225                                 throw new InvalidOperationException ("The control must implement the ICallbackEventHandler interface and provide a RaiseCallbackEvent method.");
226
227                         return GetCallbackEventReference ("'" + control.UniqueID + "'", argument, clientCallback, context, clientErrorCallback, useAsync);
228                 }
229
230                 public string GetCallbackEventReference (string target, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
231                 {
232                         RegisterWebFormClientScript ();
233
234                         if (!_initCallBackRegistered) {
235                                 _initCallBackRegistered = true;
236                                 RegisterStartupScript (typeof (Page), "WebForm_InitCallback", page.WebFormScriptReference + ".WebForm_InitCallback();", true);
237                         }
238                         return page.WebFormScriptReference + ".WebForm_DoCallback(" +
239                                 target + "," +
240                                 (argument ?? "null") + "," +
241                                 clientCallback + "," +
242                                 (context ?? "null") + "," +
243                                 (clientErrorCallback ?? "null") + "," +
244                                 (useAsync ? "true" : "false") + ")";
245                 }
246 #endif
247                 
248 #if NET_2_0
249                 public
250 #else
251                 internal
252 #endif
253                 string GetWebResourceUrl(Type type, string resourceName)
254                 {
255                         if (type == null)
256                                 throw new ArgumentNullException ("type");
257                 
258                         if (resourceName == null || resourceName.Length == 0)
259                                 throw new ArgumentNullException ("type");
260                 
261                         return System.Web.Handlers.AssemblyResourceLoader.GetResourceUrl (type, resourceName); 
262                 }
263                 
264
265                 public bool IsClientScriptBlockRegistered (string key)
266                 {
267                         return IsScriptRegistered (clientScriptBlocks, GetType(), key);
268                 }
269         
270                 public bool IsClientScriptBlockRegistered (Type type, string key)
271                 {
272                         return IsScriptRegistered (clientScriptBlocks, type, key);
273                 }
274         
275                 public bool IsStartupScriptRegistered (string key)
276                 {
277                         return IsScriptRegistered (startupScriptBlocks, GetType(), key);
278                 }
279         
280                 public bool IsStartupScriptRegistered (Type type, string key)
281                 {
282                         return IsScriptRegistered (startupScriptBlocks, type, key);
283                 }
284                 
285                 public bool IsOnSubmitStatementRegistered (string key)
286                 {
287                         return IsScriptRegistered (submitStatements, GetType(), key);
288                 }
289         
290                 public bool IsOnSubmitStatementRegistered (Type type, string key)
291                 {
292                         return IsScriptRegistered (submitStatements, type, key);
293                 }
294                 
295                 public bool IsClientScriptIncludeRegistered (string key)
296                 {
297                         return IsClientScriptIncludeRegistered (GetType (), key);
298                 }
299         
300                 public bool IsClientScriptIncludeRegistered (Type type, string key)
301                 {
302                         return IsScriptRegistered (clientScriptBlocks, type, "include-" + key);
303                 }
304                 
305                 bool IsScriptRegistered (ScriptEntry scriptList, Type type, string key)
306                 {
307                         while (scriptList != null) {
308                                 if (scriptList.Type == type && scriptList.Key == key)
309                                         return true;
310                                 scriptList = scriptList.Next;
311                         }
312                         return false;
313                 }
314                 
315                 public void RegisterArrayDeclaration (string arrayName, string arrayValue)
316                 {
317                         if (registeredArrayDeclares == null)
318                                 registeredArrayDeclares = new Hashtable();
319         
320                         if (!registeredArrayDeclares.ContainsKey (arrayName))
321                                 registeredArrayDeclares.Add (arrayName, new ArrayList());
322         
323                         ((ArrayList) registeredArrayDeclares[arrayName]).Add(arrayValue);
324                         page.RequiresFormScriptDeclaration ();
325                 }
326
327                 void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, bool addScriptTags)
328                 {
329                         RegisterScript (ref scriptList, type, key, script, addScriptTags ? ScriptEntryFormat.AddScriptTag : ScriptEntryFormat.None);
330                 }
331
332                 void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, ScriptEntryFormat format)
333                 {
334                         ScriptEntry last = null;
335                         ScriptEntry entry = scriptList;
336
337                         while (entry != null) {
338                                 if (entry.Type == type && entry.Key == key)
339                                         return;
340                                 last = entry;
341                                 entry = entry.Next;
342                         }
343
344                         entry = new ScriptEntry (type, key, script, format);
345                         
346                         if (last != null) last.Next = entry;
347                         else scriptList = entry;
348                 }
349         
350                 internal void RegisterClientScriptBlock (string key, string script)
351                 {
352                         RegisterScript (ref clientScriptBlocks, GetType(), key, script, false);
353                 }
354         
355                 public void RegisterClientScriptBlock (Type type, string key, string script)
356                 {
357                         RegisterClientScriptBlock (type, key, script, false);
358                 }
359         
360                 public void RegisterClientScriptBlock (Type type, string key, string script, bool addScriptTags)
361                 {
362                         if (type == null)
363                                 throw new ArgumentNullException ("type");
364
365                         RegisterScript (ref clientScriptBlocks, type, key, script, addScriptTags);
366                 }
367         
368                 public void RegisterHiddenField (string hiddenFieldName, string hiddenFieldInitialValue)
369                 {
370                         if (hiddenFields == null)
371                                 hiddenFields = new Hashtable ();
372
373                         if (!hiddenFields.ContainsKey (hiddenFieldName))
374                                 hiddenFields.Add (hiddenFieldName, hiddenFieldInitialValue);
375                 }
376         
377                 internal void RegisterOnSubmitStatement (string key, string script)
378                 {
379                         RegisterScript (ref submitStatements, GetType (), key, script, false);
380                 }
381         
382                 public void RegisterOnSubmitStatement (Type type, string key, string script)
383                 {
384                         if (type == null)
385                                 throw new ArgumentNullException ("type");
386                         
387                         RegisterScript (ref submitStatements, type, key, script, false);
388                 }
389         
390                 internal void RegisterStartupScript (string key, string script)
391                 {
392                         RegisterScript (ref startupScriptBlocks, GetType(), key, script, false);
393                 }
394                 
395                 public void RegisterStartupScript (Type type, string key, string script)
396                 {
397                         RegisterStartupScript (type, key, script, false);
398                 }
399                 
400                 public void RegisterStartupScript (Type type, string key, string script, bool addScriptTags)
401                 {
402                         if (type == null)
403                                 throw new ArgumentNullException ("type");
404
405                         RegisterScript (ref startupScriptBlocks, type, key, script, addScriptTags);
406                 }
407
408                 public void RegisterClientScriptInclude (string key, string url)
409                 {
410                         RegisterClientScriptInclude (GetType (), key, url);
411                 }
412                 
413                 public void RegisterClientScriptInclude (Type type, string key, string url)
414                 {
415                         if (type == null)
416                                 throw new ArgumentNullException ("type");
417                         if (url == null || url.Length == 0)
418                                 throw new ArgumentException ("url");
419
420                         RegisterScript (ref clientScriptBlocks, type, "include-" + key, url, ScriptEntryFormat.Include);
421                 }
422
423 #if NET_2_0
424                 public void RegisterClientScriptResource (Type type, string resourceName)
425                 {
426                         RegisterScript (ref clientScriptBlocks, type, "resource-" + resourceName, GetWebResourceUrl (type, resourceName), ScriptEntryFormat.Include);
427                 }
428
429                 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue)
430                 {
431                         RegisterExpandoAttribute (controlId, attributeName, attributeValue, true);
432                 }
433
434                 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue, bool encode)
435                 {
436                         if (controlId == null)
437                                 throw new ArgumentNullException ("controlId");
438
439                         if (attributeName == null)
440                                 throw new ArgumentNullException ("attributeName");
441                         
442                         if (expandoAttributes == null)
443                                 expandoAttributes = new Hashtable ();
444
445                         ListDictionary list = (ListDictionary)expandoAttributes [controlId];
446                         if (list == null) {
447                                 list = new ListDictionary ();
448                                 expandoAttributes [controlId] = list;
449                         }
450
451                         list.Add (attributeName, encode ? StrUtils.EscapeQuotesAndBackslashes (attributeValue) : attributeValue);
452                 }
453
454                 void EnsureEventValidationArray ()
455                 {
456                         if (eventValidationValues == null || eventValidationValues.Length == 0)
457                                 eventValidationValues = new int [64];
458
459                         int len = eventValidationValues.Length;
460
461                         if (eventValidationPos >= len) {
462                                 int [] tmp = new int [len * 2];
463                                 Array.Copy (eventValidationValues, tmp, len);
464                                 eventValidationValues = tmp;
465                         }
466                 }
467
468                 internal void ResetEventValidationState ()
469                 {
470                         _pageInRender = true;
471                         eventValidationPos = 0;
472                 }
473
474                 // Implemented following the description in http://odetocode.com/Blogs/scott/archive/2006/03/20/3145.aspx
475                 int CalculateEventHash (string uniqueId, string argument)
476                 {
477                         int uniqueIdHash = uniqueId.GetHashCode ();
478                         int argumentHash = String.IsNullOrEmpty (argument) ? 0 : argument.GetHashCode ();
479                         return (uniqueIdHash ^ argumentHash);
480                 }
481                 
482                 public void RegisterForEventValidation (PostBackOptions options)
483                 {
484                         // MS.NET does not check for options == null, so we won't too...
485                         RegisterForEventValidation (options.TargetControl.UniqueID, options.Argument);
486                 }
487                 
488                 public void RegisterForEventValidation (string uniqueId)
489                 {
490                         RegisterForEventValidation (uniqueId, null);
491                 }
492                 
493                 public void RegisterForEventValidation (string uniqueId, string argument)
494                 {
495                         if (!page.EnableEventValidation)
496                                 return;
497                         if (uniqueId == null || uniqueId.Length == 0)
498                                 return;
499                         if (page.IsCallback)
500                                 _hasRegisteredForEventValidationOnCallback = true;
501                         else if (!_pageInRender)
502                                 throw new InvalidOperationException ("RegisterForEventValidation may only be called from the Render method");
503
504                         EnsureEventValidationArray ();
505                         
506                         int hash = CalculateEventHash (uniqueId, argument);
507                         for (int i = 0; i < eventValidationPos; i++)
508                                 if (eventValidationValues [i] == hash)
509                                         return;
510                         eventValidationValues [eventValidationPos++] = hash;
511                 }
512
513                 public void ValidateEvent (string uniqueId)
514                 {
515                         ValidateEvent (uniqueId, null);
516                 }
517
518                 ArgumentException InvalidPostBackException ()
519                 {
520                         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.");
521                 }
522                 
523                 public void ValidateEvent (string uniqueId, string argument)
524                 {
525                         if (uniqueId == null || uniqueId.Length == 0)
526                                 throw new ArgumentException ("must not be null or empty", "uniqueId");
527                         if (!page.EnableEventValidation)
528                                 return;
529                         if (eventValidationValues == null)
530                                 throw InvalidPostBackException ();
531                         
532                         int hash = CalculateEventHash (uniqueId, argument);
533                         for (int i = 0; i < eventValidationValues.Length; i++)
534                                 if (eventValidationValues [i] == hash)
535                                         return;
536                         
537                         throw InvalidPostBackException ();
538                 }
539 #endif
540                 void WriteScripts (HtmlTextWriter writer, ScriptEntry scriptList)
541                 {
542                         if (scriptList == null)
543                                 return;
544
545                         writer.WriteLine ();
546
547                         while (scriptList != null) {
548                                 switch (scriptList.Format) {
549                                 case ScriptEntryFormat.AddScriptTag:
550                                         EnsureBeginScriptBlock (writer);
551                                         writer.Write (scriptList.Script);
552                                         break;
553                                 case ScriptEntryFormat.Include:
554                                         EnsureEndScriptBlock (writer);
555                                         WriteClientScriptInclude (writer, scriptList.Script, scriptList.Type, scriptList.Key);
556                                         break;
557                                 default:
558                                         EnsureEndScriptBlock (writer);
559                                         writer.WriteLine (scriptList.Script);
560                                         break;
561                                 }
562                                 scriptList = scriptList.Next;
563                         }
564                         EnsureEndScriptBlock (writer);
565                 }
566
567                 bool _scriptTagOpened;
568
569                 void EnsureBeginScriptBlock (HtmlTextWriter writer) {
570                         if (!_scriptTagOpened) {
571                                 WriteBeginScriptBlock (writer);
572                                 _scriptTagOpened = true;
573                         }
574                 }
575
576                 void EnsureEndScriptBlock (HtmlTextWriter writer) {
577                         if (_scriptTagOpened) {
578                                 WriteEndScriptBlock (writer);
579                                 _scriptTagOpened = false;
580                         }
581                 }
582
583 #if NET_2_0
584                 internal void RestoreEventValidationState (string fieldValue)
585                 {
586                         if (!page.EnableEventValidation || fieldValue == null || fieldValue.Length == 0)
587                                 return;
588                         IStateFormatter fmt = page.GetFormatter ();
589                         eventValidationValues = (int []) fmt.Deserialize (fieldValue);
590                         eventValidationPos = eventValidationValues.Length;
591                 }
592                 
593                 internal void SaveEventValidationState ()
594                 {
595                         if (!page.EnableEventValidation)
596                                 return;
597
598                         string eventValidation = GetEventValidationStateFormatted ();
599                         if (eventValidation == null)
600                                 return;
601
602                         RegisterHiddenField (EventStateFieldName, eventValidation);
603                 }
604
605                 internal string GetEventValidationStateFormatted ()
606                 {
607                         if (eventValidationValues == null || eventValidationValues.Length == 0)
608                                 return null;
609
610                         if(page.IsCallback && !_hasRegisteredForEventValidationOnCallback)
611                                 return null;
612
613                         IStateFormatter fmt = page.GetFormatter ();
614                         int [] array = new int [eventValidationPos];
615                         Array.Copy (eventValidationValues, array, eventValidationPos);
616                         return fmt.Serialize (array);
617                 }
618
619                 internal void WriteExpandoAttributes (HtmlTextWriter writer)
620                 {
621                         if (expandoAttributes == null)
622                                 return;
623
624                         writer.WriteLine ();
625                         WriteBeginScriptBlock (writer);
626
627                         foreach (string controlId in expandoAttributes.Keys) {
628                                 writer.WriteLine ("var {0} = document.all ? document.all [\"{0}\"] : document.getElementById (\"{0}\");", controlId);
629                                 ListDictionary attrs = (ListDictionary) expandoAttributes [controlId];
630                                 foreach (string attributeName in attrs.Keys) {
631                                         writer.WriteLine ("{0}.{1} = \"{2}\";", controlId, attributeName, attrs [attributeName]);
632                                 }
633                         }
634                         WriteEndScriptBlock (writer);
635                         writer.WriteLine ();
636                 }
637
638 #endif
639                 
640 #if NET_2_0
641                 internal const string SCRIPT_BLOCK_START = "//<![CDATA[";
642                 internal const string SCRIPT_BLOCK_END = "//]]>";
643                 internal const string SCRIPT_ELEMENT_START = @"<script type=""text/javascript"">" + SCRIPT_BLOCK_START;
644 #else
645                 internal const string SCRIPT_BLOCK_START = "<!--";
646                 internal const string SCRIPT_BLOCK_END ="// -->";
647                 internal const string SCRIPT_ELEMENT_START = @"<script language=""javascript"" type=""text/javascript"">" + SCRIPT_BLOCK_START;
648 #endif
649                 internal const string SCRIPT_ELEMENT_END = SCRIPT_BLOCK_END + "</script>";
650                 
651                 internal static void WriteBeginScriptBlock (HtmlTextWriter writer)
652                 {
653                         writer.WriteLine (SCRIPT_ELEMENT_START);
654                 }
655
656                 internal static void WriteEndScriptBlock (HtmlTextWriter writer)
657                 {
658                         writer.WriteLine (SCRIPT_ELEMENT_END);
659                 }
660                 
661                 internal void WriteHiddenFields (HtmlTextWriter writer)
662                 {
663                         if (hiddenFields == null)
664                                 return;
665
666 #if NET_2_0
667                         writer.WriteLine ();
668                         writer.RenderBeginTag (HtmlTextWriterTag.Div);
669                         int oldIndent = writer.Indent;
670                         writer.Indent = 0;
671                         bool first = true;
672 #endif
673                         foreach (string key in hiddenFields.Keys) {
674                                 string value = hiddenFields [key] as string;
675                                 if (first)
676                                         first = false;
677                                 else
678                                         writer.WriteLine ();
679 #if NET_2_0
680                                 writer.Write ("<input type=\"hidden\" name=\"{0}\" id=\"{0}\" value=\"{1}\" />", key, HttpUtility.HtmlAttributeEncode (value));
681 #else
682                                 writer.Write ("<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />", key, HttpUtility.HtmlAttributeEncode (value));
683 #endif
684                         }
685 #if NET_2_0
686                         writer.Indent = oldIndent;
687                         writer.RenderEndTag (); // DIV
688                         writer.WriteLine ();
689 #endif
690                         hiddenFields = null;
691                 }
692                 
693                 internal void WriteClientScriptInclude (HtmlTextWriter writer, string path, Type type, string key) {
694                                         if (!page.IsMultiForm)
695                                                 writer.WriteLine ("<script src=\"{0}\" type=\"text/javascript\"></script>", path);
696                                         else {
697                                                 string scriptKey = "inc_" + (type.FullName + key).GetHashCode ().ToString ("X");
698                                                 writer.WriteLine ("<script type=\"text/javascript\">");
699                                                 writer.WriteLine (SCRIPT_BLOCK_START);
700                                                 writer.WriteLine ("if (!window.{0}) {{", scriptKey);
701                                                 writer.WriteLine ("\twindow.{0} = true", scriptKey);
702                                                 writer.WriteLine ("\tdocument.write('<script src=\"{0}\" type=\"text/javascript\"><\\/script>'); }}", path);
703                                                 writer.WriteLine (SCRIPT_BLOCK_END);
704                                                 writer.WriteLine ("</script>");
705                                         }
706                 }
707                 
708                 internal void WriteClientScriptBlocks (HtmlTextWriter writer)
709                 {
710                         WriteScripts (writer, clientScriptBlocks);
711                 }
712         
713                 internal void WriteStartupScriptBlocks (HtmlTextWriter writer)
714                 {
715                         WriteScripts (writer, startupScriptBlocks);
716                 }
717         
718                 internal void WriteArrayDeclares (HtmlTextWriter writer)
719                 {
720                         if (registeredArrayDeclares != null) {
721                                 writer.WriteLine();
722                                 WriteBeginScriptBlock (writer);
723                                 IDictionaryEnumerator arrayEnum = registeredArrayDeclares.GetEnumerator();
724                                 while (arrayEnum.MoveNext()) {
725                                         if (page.IsMultiForm)
726                                                 writer.Write ("\t" + page.theForm + ".");
727                                         else
728                                                 writer.Write ("\tvar ");
729                                         writer.Write(arrayEnum.Key);
730                                         writer.Write(" =  new Array(");
731                                         IEnumerator arrayListEnum = ((ArrayList) arrayEnum.Value).GetEnumerator();
732                                         bool isFirst = true;
733                                         while (arrayListEnum.MoveNext()) {
734                                                 if (isFirst)
735                                                         isFirst = false;
736                                                 else
737                                                         writer.Write(", ");
738                                                 writer.Write(arrayListEnum.Current);
739                                         }
740                                         writer.WriteLine(");");
741                                 }
742                                 WriteEndScriptBlock (writer);
743                                 writer.WriteLine ();
744                         }
745                 }
746
747 #if NET_2_0
748                 internal string GetClientValidationEvent (string validationGroup) {
749                         if (page.IsMultiForm)
750                                 return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate('" + validationGroup + "');";
751                         return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate('" + validationGroup + "');";
752                 }
753 #endif
754
755                 internal string GetClientValidationEvent ()
756                 {
757                         if (page.IsMultiForm)
758                                 return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate();";
759                         return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate();";
760                 }
761
762
763                 internal string WriteSubmitStatements ()
764                 {
765                         if (submitStatements == null) return null;
766                         
767                         StringBuilder sb = new StringBuilder ();
768                         ScriptEntry entry = submitStatements;
769                         while (entry != null) {
770 #if NET_2_0
771                                 sb.Append (EnsureEndsWithSemicolon (entry.Script));
772 #else
773                                 sb.Append (entry.Script);
774 #endif
775                                 entry = entry.Next;
776                         }
777 #if NET_2_0
778                         RegisterClientScriptBlock (GetType(), "HtmlForm-OnSubmitStatemen",
779 @"
780 " + page.WebFormScriptReference + @".WebForm_OnSubmit = function () {
781 " + sb.ToString () + @"
782 return true;
783 }
784 ", true);
785                         return "javascript:return " + page.WebFormScriptReference + ".WebForm_OnSubmit();";
786
787 #else
788                         return sb.ToString ();
789 #endif
790                 }
791                 
792                 internal static string GetScriptLiteral (object ob)
793                 {
794                         if (ob == null)
795                                 return "null";
796                         else if (ob is string) {
797                                 string s = (string)ob;
798                                 bool escape = false;
799                                 int len = s.Length;
800
801                                 for (int i = 0; i < len; i++)
802                                         if (s [i] == '\\' || s [i] == '\"') {
803                                                 escape = true;
804                                                 break;
805                                         }
806
807                                 if (!escape)
808                                         return string.Concat ("\"", s, "\"");
809
810                                 StringBuilder sb = new StringBuilder (len + 10);
811
812                                 sb.Append ('\"');
813                                 for (int si = 0; si < len; si++) {
814                                         if (s [si] == '\"')
815                                                 sb.Append ("\\\"");
816                                         else if (s [si] == '\\')
817                                                 sb.Append ("\\\\");
818                                         else
819                                                 sb.Append (s [si]);
820                                 }
821                                 sb.Append ('\"');
822
823                                 return sb.ToString ();
824                         } else if (ob is bool) {
825                                 return ob.ToString ().ToLower (Helpers.InvariantCulture);
826                         } else {
827                                 return ob.ToString ();
828                         }
829                 }
830
831                 sealed class ScriptEntry
832                 {
833                         public readonly Type Type;
834                         public readonly string Key;
835                         public readonly string Script;
836                         public readonly ScriptEntryFormat Format;
837                         public ScriptEntry Next;
838
839                         public ScriptEntry (Type type, string key, string script, ScriptEntryFormat format) {
840                                 Key = key;
841                                 Type = type;
842                                 Script = script;
843                                 Format = format;
844                         }
845                 }
846
847                 enum ScriptEntryFormat
848                 {
849                         None,
850                         AddScriptTag,
851                         Include,
852                 }
853
854 #if NET_2_0
855                 // helper method
856                 internal static string EnsureEndsWithSemicolon (string value) {
857                         if (value != null && value.Length > 0 && value [value.Length - 1] != ';')
858                                 return value += ";";
859                         return value;
860                 }
861 #endif
862         }
863 }