Merge pull request #131 from txdv/master
[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 ownerPage;
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                 Page OwnerPage {
78                         get {
79                                 if (ownerPage == null)
80                                         throw new InvalidOperationException ("Associated Page instance is required to complete this operation.");
81                                 return ownerPage;
82                         }
83                 }
84                 
85                 internal ClientScriptManager (Page page)
86                 {
87                         this.ownerPage = page;
88                 }
89
90                 public string GetPostBackClientHyperlink (Control control, string argument)
91                 {
92                         return "javascript:" + GetPostBackEventReference (control, argument);
93                 }
94         
95                 public string GetPostBackClientHyperlink (Control control, string argument, bool registerForEventValidation)
96                 {
97                         if (registerForEventValidation)
98                                 RegisterForEventValidation (control.UniqueID, argument);
99                         return "javascript:" + GetPostBackEventReference (control, argument);
100                 }
101
102                 public string GetPostBackEventReference (Control control, string argument)
103                 {
104                         if (control == null)
105                                 throw new ArgumentNullException ("control");
106
107                         Page page = OwnerPage;
108                         page.RequiresPostBackScript ();
109                         if(page.IsMultiForm)
110                                 return page.theForm + ".__doPostBack('" + control.UniqueID + "','" + argument + "')";
111                         else
112                                 return "__doPostBack('" + control.UniqueID + "','" + argument + "')";
113                 }
114
115                 public string GetPostBackEventReference (Control control, string argument, bool registerForEventValidation)
116                 {
117                         if (control == null)
118                                 throw new ArgumentNullException ("control");
119                         
120                         if (registerForEventValidation)
121                                 RegisterForEventValidation (control.UniqueID, argument);
122                         return GetPostBackEventReference (control, argument);
123                 }
124                 
125                 public string GetPostBackEventReference (PostBackOptions options, bool registerForEventValidation)
126                 {
127                         if (options == null)
128                                 throw new ArgumentNullException ("options");
129                         if (registerForEventValidation)
130                                 RegisterForEventValidation (options);
131                         return GetPostBackEventReference (options);
132                 }
133                 
134                 public string GetPostBackEventReference (PostBackOptions options)
135                 {
136                         if (options == null)
137                                 throw new ArgumentNullException ("options");
138
139                         string actionUrl = options.ActionUrl;
140                         if (actionUrl == null && options.ValidationGroup == null && !options.TrackFocus && 
141                                 !options.AutoPostBack && !options.PerformValidation)
142                         {
143                                 if (!options.ClientSubmit)
144                                         return null;
145
146                                 if (options.RequiresJavaScriptProtocol)
147                                         return GetPostBackClientHyperlink (options.TargetControl, options.Argument);
148                                 else
149                                         return GetPostBackEventReference (options.TargetControl, options.Argument);
150                         }
151
152                         RegisterWebFormClientScript ();
153
154                         Page page = OwnerPage;
155                         HttpRequest req = page.RequestInternal;
156                         Uri pageUrl = req != null ? req.Url : null;
157                         if (pageUrl != null)
158                                 RegisterHiddenField (Page.PreviousPageID, pageUrl.AbsolutePath);
159
160                         if(options.TrackFocus)
161                                 RegisterHiddenField (Page.LastFocusID, String.Empty);
162
163                         string prefix = options.RequiresJavaScriptProtocol ? "javascript:" : String.Empty;
164                         if (page.IsMultiForm)
165                                 prefix += page.theForm + ".";
166
167                         return prefix + "WebForm_DoPostback(" +
168                                 ClientScriptManager.GetScriptLiteral (options.TargetControl.UniqueID) + "," +
169                                 ClientScriptManager.GetScriptLiteral (options.Argument) + "," +
170                                 ClientScriptManager.GetScriptLiteral (actionUrl) + "," +
171                                 ClientScriptManager.GetScriptLiteral (options.AutoPostBack) + "," +
172                                 ClientScriptManager.GetScriptLiteral (options.PerformValidation) + "," +
173                                 ClientScriptManager.GetScriptLiteral (options.TrackFocus) + "," +
174                                 ClientScriptManager.GetScriptLiteral (options.ClientSubmit) + "," +
175                                 ClientScriptManager.GetScriptLiteral (options.ValidationGroup) + ")";
176                 }
177
178                 internal void RegisterWebFormClientScript ()
179                 {
180                         if (_webFormClientScriptRequired)
181                                 return;
182
183                         OwnerPage.RequiresPostBackScript ();
184                         _webFormClientScriptRequired = true;
185                 }
186
187                 internal void WriteWebFormClientScript (HtmlTextWriter writer) {
188                         if (!_webFormClientScriptRendered && _webFormClientScriptRequired) {
189                                 Page page = OwnerPage;
190                                 writer.WriteLine ();
191                                 WriteClientScriptInclude (writer, GetWebResourceUrl (typeof (Page), "webform.js"), typeof (Page), "webform.js");
192                                 WriteBeginScriptBlock (writer);
193                                 writer.WriteLine ("WebForm_Initialize({0});", page.IsMultiForm ? page.theForm : "window");
194                                 WriteEndScriptBlock (writer);
195                                 _webFormClientScriptRendered = true;
196                         }
197                 }
198                 
199                 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context)
200                 {
201                         return GetCallbackEventReference (control, argument, clientCallback, context, null, false);
202                 }
203
204                 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, bool useAsync)
205                 {
206                         return GetCallbackEventReference (control, argument, clientCallback, context, null, useAsync);
207                 }
208
209                 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
210                 {
211                         if (control == null)
212                                 throw new ArgumentNullException ("control");
213                         if(!(control is ICallbackEventHandler))
214                                 throw new InvalidOperationException ("The control must implement the ICallbackEventHandler interface and provide a RaiseCallbackEvent method.");
215
216                         return GetCallbackEventReference ("'" + control.UniqueID + "'", argument, clientCallback, context, clientErrorCallback, useAsync);
217                 }
218
219                 public string GetCallbackEventReference (string target, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
220                 {
221                         RegisterWebFormClientScript ();
222
223                         Page page = OwnerPage;
224                         if (!_initCallBackRegistered) {
225                                 _initCallBackRegistered = true;
226                                 RegisterStartupScript (typeof (Page), "WebForm_InitCallback", page.WebFormScriptReference + ".WebForm_InitCallback();", true);
227                         }
228                         return page.WebFormScriptReference + ".WebForm_DoCallback(" +
229                                 target + "," +
230                                 (argument ?? "null") + "," +
231                                 clientCallback + "," +
232                                 (context ?? "null") + "," +
233                                 (clientErrorCallback ?? "null") + "," +
234                                 (useAsync ? "true" : "false") + ")";
235                 }
236                 
237                 public string GetWebResourceUrl(Type type, string resourceName)
238                 {
239                         if (type == null)
240                                 throw new ArgumentNullException ("type");
241                 
242                         if (resourceName == null || resourceName.Length == 0)
243                                 throw new ArgumentNullException ("type");
244                 
245                         return System.Web.Handlers.AssemblyResourceLoader.GetResourceUrl (type, resourceName); 
246                 }
247                 
248
249                 public bool IsClientScriptBlockRegistered (string key)
250                 {
251                         return IsScriptRegistered (clientScriptBlocks, GetType(), key);
252                 }
253         
254                 public bool IsClientScriptBlockRegistered (Type type, string key)
255                 {
256                         return IsScriptRegistered (clientScriptBlocks, type, key);
257                 }
258         
259                 public bool IsStartupScriptRegistered (string key)
260                 {
261                         return IsScriptRegistered (startupScriptBlocks, GetType(), key);
262                 }
263         
264                 public bool IsStartupScriptRegistered (Type type, string key)
265                 {
266                         return IsScriptRegistered (startupScriptBlocks, type, key);
267                 }
268                 
269                 public bool IsOnSubmitStatementRegistered (string key)
270                 {
271                         return IsScriptRegistered (submitStatements, GetType(), key);
272                 }
273         
274                 public bool IsOnSubmitStatementRegistered (Type type, string key)
275                 {
276                         return IsScriptRegistered (submitStatements, type, key);
277                 }
278                 
279                 public bool IsClientScriptIncludeRegistered (string key)
280                 {
281                         return IsClientScriptIncludeRegistered (GetType (), key);
282                 }
283         
284                 public bool IsClientScriptIncludeRegistered (Type type, string key)
285                 {
286                         return IsScriptRegistered (clientScriptBlocks, type, "include-" + key);
287                 }
288                 
289                 bool IsScriptRegistered (ScriptEntry scriptList, Type type, string key)
290                 {
291                         while (scriptList != null) {
292                                 if (scriptList.Type == type && scriptList.Key == key)
293                                         return true;
294                                 scriptList = scriptList.Next;
295                         }
296                         return false;
297                 }
298                 
299                 public void RegisterArrayDeclaration (string arrayName, string arrayValue)
300                 {
301                         if (registeredArrayDeclares == null)
302                                 registeredArrayDeclares = new Hashtable();
303         
304                         if (!registeredArrayDeclares.ContainsKey (arrayName))
305                                 registeredArrayDeclares.Add (arrayName, new ArrayList());
306         
307                         ((ArrayList) registeredArrayDeclares[arrayName]).Add(arrayValue);
308                         OwnerPage.RequiresFormScriptDeclaration ();
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                 public void RegisterClientScriptResource (Type type, string resourceName)
408                 {
409                         RegisterScript (ref clientScriptBlocks, type, "resource-" + resourceName, GetWebResourceUrl (type, resourceName), ScriptEntryFormat.Include);
410                 }
411
412                 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue)
413                 {
414                         RegisterExpandoAttribute (controlId, attributeName, attributeValue, true);
415                 }
416
417                 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue, bool encode)
418                 {
419                         if (controlId == null)
420                                 throw new ArgumentNullException ("controlId");
421
422                         if (attributeName == null)
423                                 throw new ArgumentNullException ("attributeName");
424                         
425                         if (expandoAttributes == null)
426                                 expandoAttributes = new Hashtable ();
427
428                         ListDictionary list = (ListDictionary)expandoAttributes [controlId];
429                         if (list == null) {
430                                 list = new ListDictionary ();
431                                 expandoAttributes [controlId] = list;
432                         }
433
434                         list.Add (attributeName, encode ? StrUtils.EscapeQuotesAndBackslashes (attributeValue) : attributeValue);
435                 }
436
437                 void EnsureEventValidationArray ()
438                 {
439                         if (eventValidationValues == null || eventValidationValues.Length == 0)
440                                 eventValidationValues = new int [64];
441
442                         int len = eventValidationValues.Length;
443
444                         if (eventValidationPos >= len) {
445                                 int [] tmp = new int [len * 2];
446                                 Array.Copy (eventValidationValues, tmp, len);
447                                 eventValidationValues = tmp;
448                         }
449                 }
450
451                 internal void ResetEventValidationState ()
452                 {
453                         _pageInRender = true;
454                         eventValidationPos = 0;
455                 }
456
457                 // Implemented following the description in http://odetocode.com/Blogs/scott/archive/2006/03/20/3145.aspx
458                 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                         Page page = OwnerPage;
479                         if (!page.EnableEventValidation)
480                                 return;
481                         if (uniqueId == null || uniqueId.Length == 0)
482                                 return;
483                         if (page.IsCallback)
484                                 _hasRegisteredForEventValidationOnCallback = true;
485                         else if (!_pageInRender)
486                                 throw new InvalidOperationException ("RegisterForEventValidation may only be called from the Render method");
487
488                         EnsureEventValidationArray ();
489                         
490                         int hash = CalculateEventHash (uniqueId, argument);
491                         for (int i = 0; i < eventValidationPos; i++)
492                                 if (eventValidationValues [i] == hash)
493                                         return;
494                         eventValidationValues [eventValidationPos++] = hash;
495                 }
496
497                 public void ValidateEvent (string uniqueId)
498                 {
499                         ValidateEvent (uniqueId, null);
500                 }
501
502                 ArgumentException InvalidPostBackException ()
503                 {
504                         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.");
505                 }
506                 
507                 public void ValidateEvent (string uniqueId, string argument)
508                 {
509                         if (uniqueId == null || uniqueId.Length == 0)
510                                 throw new ArgumentException ("must not be null or empty", "uniqueId");
511                         if (!OwnerPage.EnableEventValidation)
512                                 return;
513                         if (eventValidationValues == null)
514                                 throw InvalidPostBackException ();
515                         
516                         int hash = CalculateEventHash (uniqueId, argument);
517                         for (int i = 0; i < eventValidationValues.Length; i++)
518                                 if (eventValidationValues [i] == hash)
519                                         return;
520                         
521                         throw InvalidPostBackException ();
522                 }
523
524                 void WriteScripts (HtmlTextWriter writer, ScriptEntry scriptList)
525                 {
526                         if (scriptList == null)
527                                 return;
528
529                         writer.WriteLine ();
530
531                         while (scriptList != null) {
532                                 switch (scriptList.Format) {
533                                 case ScriptEntryFormat.AddScriptTag:
534                                         EnsureBeginScriptBlock (writer);
535                                         writer.Write (scriptList.Script);
536                                         break;
537                                 case ScriptEntryFormat.Include:
538                                         EnsureEndScriptBlock (writer);
539                                         WriteClientScriptInclude (writer, scriptList.Script, scriptList.Type, scriptList.Key);
540                                         break;
541                                 default:
542                                         EnsureEndScriptBlock (writer);
543                                         writer.WriteLine (scriptList.Script);
544                                         break;
545                                 }
546                                 scriptList = scriptList.Next;
547                         }
548                         EnsureEndScriptBlock (writer);
549                 }
550
551                 bool _scriptTagOpened;
552
553                 void EnsureBeginScriptBlock (HtmlTextWriter writer) {
554                         if (!_scriptTagOpened) {
555                                 WriteBeginScriptBlock (writer);
556                                 _scriptTagOpened = true;
557                         }
558                 }
559
560                 void EnsureEndScriptBlock (HtmlTextWriter writer) {
561                         if (_scriptTagOpened) {
562                                 WriteEndScriptBlock (writer);
563                                 _scriptTagOpened = false;
564                         }
565                 }
566
567                 internal void RestoreEventValidationState (string fieldValue)
568                 {
569                         Page page = OwnerPage;
570                         if (!page.EnableEventValidation || fieldValue == null || fieldValue.Length == 0)
571                                 return;
572                         IStateFormatter fmt = page.GetFormatter ();
573                         eventValidationValues = (int []) fmt.Deserialize (fieldValue);
574                         eventValidationPos = eventValidationValues.Length;
575                 }
576                 
577                 internal void SaveEventValidationState ()
578                 {
579                         if (!OwnerPage.EnableEventValidation)
580                                 return;
581
582                         string eventValidation = GetEventValidationStateFormatted ();
583                         if (eventValidation == null)
584                                 return;
585
586                         RegisterHiddenField (EventStateFieldName, eventValidation);
587                 }
588
589                 internal string GetEventValidationStateFormatted ()
590                 {
591                         if (eventValidationValues == null || eventValidationValues.Length == 0)
592                                 return null;
593
594                         Page page = OwnerPage;
595                         if(page.IsCallback && !_hasRegisteredForEventValidationOnCallback)
596                                 return null;
597
598                         IStateFormatter fmt = page.GetFormatter ();
599                         int [] array = new int [eventValidationPos];
600                         Array.Copy (eventValidationValues, array, eventValidationPos);
601                         return fmt.Serialize (array);
602                 }
603
604                 internal void WriteExpandoAttributes (HtmlTextWriter writer)
605                 {
606                         if (expandoAttributes == null)
607                                 return;
608
609                         writer.WriteLine ();
610                         WriteBeginScriptBlock (writer);
611
612                         foreach (string controlId in expandoAttributes.Keys) {
613                                 writer.WriteLine ("var {0} = document.all ? document.all [\"{0}\"] : document.getElementById (\"{0}\");", controlId);
614                                 ListDictionary attrs = (ListDictionary) expandoAttributes [controlId];
615                                 foreach (string attributeName in attrs.Keys) {
616                                         writer.WriteLine ("{0}.{1} = \"{2}\";", controlId, attributeName, attrs [attributeName]);
617                                 }
618                         }
619                         WriteEndScriptBlock (writer);
620                         writer.WriteLine ();
621                 }
622
623                 internal const string SCRIPT_BLOCK_START = "//<![CDATA[";
624                 internal const string SCRIPT_BLOCK_END = "//]]>";
625                 internal const string SCRIPT_ELEMENT_START = @"<script type=""text/javascript"">" + SCRIPT_BLOCK_START;
626                 internal const string SCRIPT_ELEMENT_END = SCRIPT_BLOCK_END + "</script>";
627                 
628                 internal static void WriteBeginScriptBlock (HtmlTextWriter writer)
629                 {
630                         writer.WriteLine (SCRIPT_ELEMENT_START);
631                 }
632
633                 internal static void WriteEndScriptBlock (HtmlTextWriter writer)
634                 {
635                         writer.WriteLine (SCRIPT_ELEMENT_END);
636                 }
637                 
638                 internal void WriteHiddenFields (HtmlTextWriter writer)
639                 {
640                         if (hiddenFields == null)
641                                 return;
642
643                         writer.WriteLine ();
644 #if NET_4_0
645                         writer.AddAttribute (HtmlTextWriterAttribute.Class, "aspNetHidden");
646 #endif
647                         writer.RenderBeginTag (HtmlTextWriterTag.Div);
648                         int oldIndent = writer.Indent;
649                         writer.Indent = 0;
650                         bool first = true;
651                         var sb = new StringBuilder ();
652                         
653                         foreach (string key in hiddenFields.Keys) {
654                                 string value = hiddenFields [key] as string;
655                                 if (first)
656                                         first = false;
657                                 else
658                                         writer.WriteLine ();
659                                 sb.Append ("<input type=\"hidden\" name=\"");
660                                 sb.Append (key);
661                                 sb.Append ("\" id=\"");
662                                 sb.Append (key);
663                                 sb.Append ("\" value=\"");
664                                 sb.Append (HttpUtility.HtmlAttributeEncode (value));
665                                 sb.Append ("\" />");
666                         }
667                         writer.Write (sb.ToString ());
668                         writer.Indent = oldIndent;
669                         writer.RenderEndTag (); // DIV
670                         writer.WriteLine ();
671                         hiddenFields = null;
672                 }
673                 
674                 internal void WriteClientScriptInclude (HtmlTextWriter writer, string path, Type type, string key) {
675                                         if (!OwnerPage.IsMultiForm)
676                                                 writer.WriteLine ("<script src=\"{0}\" type=\"text/javascript\"></script>", path);
677                                         else {
678                                                 string scriptKey = "inc_" + (type.FullName + key).GetHashCode ().ToString ("X");
679                                                 writer.WriteLine ("<script type=\"text/javascript\">");
680                                                 writer.WriteLine (SCRIPT_BLOCK_START);
681                                                 writer.WriteLine ("if (!window.{0}) {{", scriptKey);
682                                                 writer.WriteLine ("\twindow.{0} = true", scriptKey);
683                                                 writer.WriteLine ("\tdocument.write('<script src=\"{0}\" type=\"text/javascript\"><\\/script>'); }}", path);
684                                                 writer.WriteLine (SCRIPT_BLOCK_END);
685                                                 writer.WriteLine ("</script>");
686                                         }
687                 }
688                 
689                 internal void WriteClientScriptBlocks (HtmlTextWriter writer)
690                 {
691                         WriteScripts (writer, clientScriptBlocks);
692                 }
693         
694                 internal void WriteStartupScriptBlocks (HtmlTextWriter writer)
695                 {
696                         WriteScripts (writer, startupScriptBlocks);
697                 }
698         
699                 internal void WriteArrayDeclares (HtmlTextWriter writer)
700                 {
701                         if (registeredArrayDeclares != null) {
702                                 writer.WriteLine();
703                                 WriteBeginScriptBlock (writer);
704                                 IDictionaryEnumerator arrayEnum = registeredArrayDeclares.GetEnumerator();
705                                 Page page = OwnerPage;
706                                 while (arrayEnum.MoveNext()) {
707                                         if (page.IsMultiForm)
708                                                 writer.Write ("\t" + page.theForm + ".");
709                                         else
710                                                 writer.Write ("\tvar ");
711                                         writer.Write(arrayEnum.Key);
712                                         writer.Write(" =  new Array(");
713                                         IEnumerator arrayListEnum = ((ArrayList) arrayEnum.Value).GetEnumerator();
714                                         bool isFirst = true;
715                                         while (arrayListEnum.MoveNext()) {
716                                                 if (isFirst)
717                                                         isFirst = false;
718                                                 else
719                                                         writer.Write(", ");
720                                                 writer.Write(arrayListEnum.Current);
721                                         }
722                                         writer.WriteLine(");");
723                                 }
724                                 WriteEndScriptBlock (writer);
725                                 writer.WriteLine ();
726                         }
727                 }
728
729                 internal string GetClientValidationEvent (string validationGroup) {
730                         Page page = OwnerPage;
731                         if (page.IsMultiForm)
732                                 return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate('" + validationGroup + "');";
733                         return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate('" + validationGroup + "');";
734                 }
735
736                 internal string GetClientValidationEvent ()
737                 {
738                         Page page = OwnerPage;
739                         if (page.IsMultiForm)
740                                 return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate();";
741                         return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate();";
742                 }
743
744
745                 internal string WriteSubmitStatements ()
746                 {
747                         if (submitStatements == null) return null;
748                         
749                         StringBuilder sb = new StringBuilder ();
750                         ScriptEntry entry = submitStatements;
751                         while (entry != null) {
752                                 sb.Append (EnsureEndsWithSemicolon (entry.Script));
753                                 entry = entry.Next;
754                         }
755                         Page page = OwnerPage;
756                         RegisterClientScriptBlock (GetType(), "HtmlForm-OnSubmitStatemen",
757 @"
758 " + page.WebFormScriptReference + @".WebForm_OnSubmit = function () {
759 " + sb.ToString () + @"
760 return true;
761 }
762 ", true);
763                         return "javascript:return " + page.WebFormScriptReference + ".WebForm_OnSubmit();";
764                 }
765                 
766                 internal static string GetScriptLiteral (object ob)
767                 {
768                         if (ob == null)
769                                 return "null";
770                         else if (ob is string) {
771                                 string s = (string)ob;
772                                 bool escape = false;
773                                 int len = s.Length;
774
775                                 for (int i = 0; i < len; i++)
776                                         if (s [i] == '\\' || s [i] == '\"') {
777                                                 escape = true;
778                                                 break;
779                                         }
780
781                                 if (!escape)
782                                         return string.Concat ("\"", s, "\"");
783
784                                 StringBuilder sb = new StringBuilder (len + 10);
785
786                                 sb.Append ('\"');
787                                 for (int si = 0; si < len; si++) {
788                                         if (s [si] == '\"')
789                                                 sb.Append ("\\\"");
790                                         else if (s [si] == '\\')
791                                                 sb.Append ("\\\\");
792                                         else
793                                                 sb.Append (s [si]);
794                                 }
795                                 sb.Append ('\"');
796
797                                 return sb.ToString ();
798                         } else if (ob is bool) {
799                                 return ob.ToString ().ToLower (Helpers.InvariantCulture);
800                         } else {
801                                 return ob.ToString ();
802                         }
803                 }
804
805                 sealed class ScriptEntry
806                 {
807                         public readonly Type Type;
808                         public readonly string Key;
809                         public readonly string Script;
810                         public readonly ScriptEntryFormat Format;
811                         public ScriptEntry Next;
812
813                         public ScriptEntry (Type type, string key, string script, ScriptEntryFormat format) {
814                                 Key = key;
815                                 Type = type;
816                                 Script = script;
817                                 Format = format;
818                         }
819                 }
820
821                 enum ScriptEntryFormat
822                 {
823                         None,
824                         AddScriptTag,
825                         Include,
826                 }
827
828                 // helper method
829                 internal static string EnsureEndsWithSemicolon (string value) {
830                         if (value != null && value.Length > 0 && value [value.Length - 1] != ';')
831                                 return value += ";";
832                         return value;
833                 }
834         }
835 }