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