Fixing an issue with output parameters that are of type IntPtr
[castle.git] / MonoRail / Castle.MonoRail.Framework / Helpers / AjaxHelper.cs
blob394d3b1a63dab5e4c54b4bb106920342845dd624
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.MonoRail.Framework.Helpers
17 using System;
18 using System.Text;
19 using System.Collections;
20 using System.Collections.Specialized;
21 using Castle.Core;
22 using Castle.MonoRail.Framework.Internal;
23 using Castle.MonoRail.Framework.Services.AjaxProxyGenerator;
25 /// <summary>
26 /// XmlHttpRequest supported events.
27 /// </summary>
28 public enum CallbackEnum
30 /// <summary>
31 /// Not initialized
32 /// </summary>
33 Uninitialized,
34 /// <summary>
35 /// Called when the remote document is being
36 /// loaded with data by the browser.
37 /// </summary>
38 Loading,
39 /// <summary>
40 /// Called when the browser has finished loading
41 /// the remote document.
42 /// </summary>
43 Loaded,
44 /// <summary>
45 /// Called when the user can interact with the
46 /// remote document, even though it has not
47 /// finished loading.
48 /// </summary>
49 Interactive,
50 /// <summary>
51 /// Called when the XMLHttpRequest has completed.
52 /// </summary>
53 Complete,
54 /// <summary>
55 /// Called when the request was successfully (Status code &lt; 500)
56 /// </summary>
57 OnSuccess,
58 /// <summary>
59 /// Called when the request was not successfully (Status code &gt;= 500)
60 /// </summary>
61 OnFailure
64 /// <summary>
65 /// MonoRail Helper that delivers AJAX capabilities.
66 /// </summary>
67 /// <remarks>
68 /// The following libraries are exposed:
69 /// <list type="table">
70 /// <item><term> Prototype </term>
71 /// <description> Simplify ajax programming, among other goodies
72 /// </description></item>
73 /// <item><term> Behaviour </term>
74 /// <description> Uses css selectors to bind javascript code to DOM elements
75 /// </description></item>
76 /// </list>
77 /// </remarks>
78 public class AjaxHelper : AbstractHelper, IServiceEnabledComponent
80 private IAjaxProxyGenerator ajaxProxyGenerator;
82 #region Constructors
83 /// <summary>
84 /// Initializes a new instance of the <see cref="AjaxHelper"/> class.
85 /// </summary>
86 public AjaxHelper() { }
87 /// <summary>
88 /// Initializes a new instance of the <see cref="AjaxHelper"/> class.
89 /// setting the Controller, Context and ControllerContext.
90 /// </summary>
91 /// <param name="engineContext">The engine context.</param>
92 public AjaxHelper(IEngineContext engineContext) : base(engineContext) { }
93 #endregion
95 #region IServiceEnabledComponent implementation
97 /// <summary>
98 /// Invoked by the framework in order to give a chance to
99 /// obtain other services
100 /// </summary>
101 /// <param name="provider">The service proviver</param>
102 public void Service(IServiceProvider provider)
104 ajaxProxyGenerator = (IAjaxProxyGenerator) provider.GetService(typeof(IAjaxProxyGenerator));
107 #endregion
109 #region Scripts
111 /// <summary>
112 /// Renders a Javascript library inside a single script tag.
113 /// </summary>
114 public String InstallScripts()
116 return RenderScriptBlockToSource("/MonoRail/Files/AjaxScripts");
119 /// <summary>
120 /// Renders a Javascript library inside a single script tag.
121 /// </summary>
122 [Obsolete("Please use the preferred InstallScripts function.")]
123 public String GetJavascriptFunctions()
125 return InstallScripts();
128 #endregion
130 #region GenerateJSProxy overloads
132 /// <summary>
133 /// Generates an AJAX JavaScript proxy for the current controller.
134 /// </summary>
135 /// <param name="proxyName">Name of the javascript proxy object</param>
136 public String GenerateJSProxy(string proxyName)
138 return GenerateJSProxy(proxyName, ControllerContext.AreaName, ControllerContext.Name);
141 /// <summary>
142 /// Generates an AJAX JavaScript proxy for a given controller.
143 /// </summary>
144 /// <param name="proxyName">Name of the javascript proxy object</param>
145 /// <param name="controller">Controller which will be target of the proxy</param>
146 public String GenerateJSProxy(string proxyName, string controller)
148 return GenerateJSProxy(proxyName, String.Empty, controller);
151 /// <summary>
152 /// Generates an AJAX JavaScript proxy for a given controller.
153 /// </summary>
154 /// <param name="proxyName">Name of the javascript proxy object</param>
155 /// <param name="controller">Controller which will be target of the proxy</param>
156 /// <param name="area">area which the controller belongs to</param>
157 public String GenerateJSProxy(string proxyName, string area, string controller)
159 return ajaxProxyGenerator.GenerateJSProxy(CurrentContext, proxyName, area, controller);
162 #endregion
164 #region LinkToFunction
166 /// <summary>
167 /// Returns a link that will trigger a javascript function using the
168 /// onclick handler and return false after the fact.
169 /// <code>
170 /// &lt;a href="javascript:void(0);" onclick="functionCodeOrName; return false"&gt;innerContent&lt;/a&gt;
171 /// </code>
172 /// </summary>
173 /// <param name="innerContent">Link content</param>
174 /// <param name="functionCodeOrName">Function definition</param>
175 /// <param name="attributes">Attributes to be applied to the html element</param>
176 /// <returns></returns>
177 public String LinkToFunction(String innerContent, String functionCodeOrName, IDictionary attributes)
179 String htmlAtt = GetAttributes(attributes);
181 return String.Format("<a href=\"javascript:void(0);\" {2} onclick=\"{0}; return false;\" >{1}</a>", functionCodeOrName, innerContent, htmlAtt );
184 /// <summary>
185 /// Returns a link that will trigger a javascript function using the
186 /// onclick handler and return false after the fact.
187 /// <code>
188 /// &lt;a href="javascript:void(0);" onclick="confirm('question') { functionCodeOrName}; return false"&gt;innerContent&lt;/a&gt;
189 /// </code>
190 /// </summary>
191 /// <param name="innerContent">Link content</param>
192 /// <param name="functionCodeOrName">Function definition</param>
193 /// <param name="confirm">Confirm question</param>
194 /// <param name="attributes">Attributes to be applied to the html element</param>
195 /// <returns></returns>
196 public String LinkToFunction(String innerContent, String functionCodeOrName, string confirm, IDictionary attributes)
198 String htmlAtt = GetAttributes(attributes);
200 return String.Format("<a href=\"javascript:void(0);\" {2} onclick=\"if(confirm('" + confirm + "')){{{0}}};return false;\" >{1}</a>", functionCodeOrName, innerContent, htmlAtt);
203 /// <summary>
204 /// Returns a link that will trigger a javascript function using the
205 /// onclick handler and return false after the fact.
206 /// </summary>
207 /// <param name="innerContent">Link content</param>
208 /// <param name="functionCodeOrName">Function definition</param>
209 /// <returns></returns>
210 public String LinkToFunction(String innerContent, String functionCodeOrName)
212 return LinkToFunction(innerContent, functionCodeOrName, new Hashtable());
215 /// <summary>
216 /// Returns a link that will trigger a javascript function using the
217 /// onclick handler and return false after the fact.
218 /// </summary>
219 /// <param name="innerContent">Link content</param>
220 /// <param name="functionCodeOrName">Function definition</param>
221 /// <param name="confirm">Confirm question</param>
222 /// <returns></returns>
223 public String LinkToFunction(String innerContent, String functionCodeOrName, String confirm)
225 return LinkToFunction(innerContent, functionCodeOrName, confirm, null);
228 #endregion
230 #region ButtonToFunction
232 /// <summary>
233 /// Returns a button that will trigger a javascript function using the
234 /// onclick handler and return false after the fact.
235 /// </summary>
236 /// <param name="innerContent">Button legend</param>
237 /// <param name="functionCodeOrName">Function definition or name</param>
238 /// <param name="attributes">Attributes to be applied to the input html element</param>
239 /// <returns></returns>
240 public String ButtonToFunction(String innerContent, String functionCodeOrName, IDictionary attributes)
242 String htmlAtt = GetAttributes(attributes);
244 return String.Format("<input type=\"button\" {2} onclick=\"{0}; return false;\" value=\"{1}\" />",
245 functionCodeOrName, innerContent, htmlAtt);
248 /// <summary>
249 /// Returns a button that will trigger a javascript function using the
250 /// onclick handler and return false after the fact.
251 /// </summary>
252 /// <param name="innerContent">Button legend</param>
253 /// <param name="functionCodeOrName">Function definition or name</param>
254 /// <returns></returns>
255 public String ButtonToFunction(String innerContent, String functionCodeOrName)
257 return ButtonToFunction(innerContent, functionCodeOrName, null);
260 #endregion
262 #region ButtonToRemote
264 /// <summary>
265 /// Creates a button that if clicked will fire an Ajax invocation.
266 /// </summary>
267 /// <param name="innerContent">Button legend</param>
268 /// <param name="url">The URL of the Ajax action</param>
269 /// <param name="options">the options for the Ajax invocation</param>
270 /// <returns>The handcrafted input</returns>
271 public String ButtonToRemote(String innerContent, String url, IDictionary options)
273 return ButtonToFunction(innerContent, BuildRemoteFunction(url, options));
276 /// <summary>
277 /// Creates a button that if clicked will fire an Ajax invocation.
278 /// </summary>
279 /// <param name="innerContent">Button legend</param>
280 /// <param name="url">the target url</param>
281 /// <param name="options">the options for the Ajax invocation</param>
282 /// <param name="htmloptions">Attributes to be applied to the html element</param>
283 /// <returns>The handcrafted input</returns>
284 public String ButtonToRemote(String innerContent, String url, IDictionary options, IDictionary htmloptions)
286 return ButtonToFunction(innerContent, BuildRemoteFunction(url, options), htmloptions);
289 #endregion
291 #region LinkToRemote
293 /// <summary>
294 /// Returns a link to a remote action defined by <c>options["url"]</c>
295 /// that is called in the background using
296 /// XMLHttpRequest. The result of that request can then be inserted into a
297 /// DOM object whose id can be specified with <c>options["update"]</c>.
298 /// Usually, the result would be a partial prepared by the controller
299 /// </summary>
300 /// <param name="innerContent">Link content</param>
301 /// <param name="url">Target url</param>
302 /// <param name="options">the options for the Ajax invocation</param>
303 /// <returns>The handcrafted element</returns>
304 public String LinkToRemote(String innerContent, String url, IDictionary options)
306 return LinkToFunction(innerContent, BuildRemoteFunction(url, options));
309 /// <summary>
310 /// Returns a link to a remote action defined by <c>options["url"]</c>
311 /// that is called in the background using
312 /// XMLHttpRequest. The result of that request can then be inserted into a
313 /// DOM object whose id can be specified with <c>options["update"]</c>.
314 /// Usually, the result would be a partial prepared by the controller
315 /// </summary>
316 /// <param name="innerContent">Link content</param>
317 /// <param name="confirm">the confirm question</param>
318 /// <param name="url">Target url</param>
319 /// <param name="options">the options for the Ajax invocation</param>
320 /// <returns>The handcrafted element</returns>
321 public String LinkToRemote(String innerContent, String confirm, String url, IDictionary options)
323 return LinkToFunction(innerContent, BuildRemoteFunction(url, options), confirm, null);
326 /// <summary>
327 /// Returns a link to a remote action defined by <c>options["url"]</c>
328 /// that is called in the background using
329 /// XMLHttpRequest. The result of that request can then be inserted into a
330 /// DOM object whose id can be specified with <c>options["update"]</c>.
331 /// Usually, the result would be a partial prepared by the controller
332 /// </summary>
333 /// <param name="innerContent">Link content</param>
334 /// <param name="url">Target url</param>
335 /// <param name="options">the options for the Ajax invocation</param>
336 /// <param name="htmloptions">Attributes to be applied to the html element</param>
337 /// <returns>The handcrafted element</returns>
338 public String LinkToRemote(String innerContent, String url, IDictionary options, IDictionary htmloptions)
340 return LinkToFunction(innerContent, BuildRemoteFunction(url, options), htmloptions);
343 #endregion
345 #region BuildFormRemoteTag
347 /// <summary>
348 /// Returns a form tag that will submit using XMLHttpRequest
349 /// in the background instead of the regular
350 /// reloading POST arrangement. Even though it is
351 /// using Javascript to serialize the form elements, the form submission
352 /// will work just like a regular submission as viewed by the
353 /// receiving side (all elements available).
354 /// </summary>
355 /// <param name="url">Target url</param>
356 /// <param name="options">the options for the Ajax invocation</param>
357 /// <returns>The handcrafted element</returns>
358 public String BuildFormRemoteTag(String url, IDictionary options)
360 return BuildFormRemoteTag( GetOptions(url, options) );
363 /// <summary>
364 /// Returns a form tag that will submit using XMLHttpRequest
365 /// in the background instead of the regular
366 /// reloading POST arrangement. Even though it is
367 /// using Javascript to serialize the form elements, the form submission
368 /// will work just like a regular submission as viewed by the
369 /// receiving side (all elements available).
370 /// </summary>
371 /// <param name="options">the options for the Ajax invocation</param>
372 /// <returns>The handcrafted element</returns>
373 public String BuildFormRemoteTag(IDictionary options)
375 options["form"] = true;
377 String remoteFunc = RemoteFunction(options);
379 String formId = options.Contains("formId") ? ("id=\"" + (String) options["formId"] + "\"") : String.Empty;
381 return String.Format("<form {1} onsubmit=\"{0}; return false;\" enctype=\"multipart/form-data\" action=\"{2}\" method=\"post\" >", remoteFunc, formId, options["url"]);
384 #endregion
386 #region ObserveField
388 /// <summary>
389 /// Observes the field with the DOM ID specified by <c>fieldId</c> and makes
390 /// an Ajax when its contents have changed.
391 /// </summary>
392 /// <param name="fieldId">Form field to be observed</param>
393 /// <param name="frequency">The frequency (in seconds) at which changes to
394 /// this field will be detected. (required)</param>
395 /// <param name="url">url for the action to call
396 /// when the field has changed (required)</param>
397 /// <param name="idOfElementToBeUpdated"> Specifies the DOM ID of the element whose
398 /// innerHTML should be updated with the
399 /// XMLHttpRequest response text.</param>
400 /// <param name="with">A Javascript expression specifying the
401 /// parameters for the XMLHttpRequest. This defaults
402 /// to 'value', which in the evaluated context
403 /// refers to the new field value.</param>
404 /// <returns>javascript that activates the observer</returns>
405 public String ObserveField(String fieldId, int frequency, String url, String idOfElementToBeUpdated, String with)
407 IDictionary options = new HybridDictionary();
408 options["frequency"] = frequency;
409 options["url"] = url;
411 if (idOfElementToBeUpdated != null) options["update"] = idOfElementToBeUpdated;
412 if (with != null) options["with"] = with;
414 return BuildObserver("Form.Element.Observer", fieldId, options);
417 /// <summary>
418 /// Observes the field with the DOM ID specified by <c>fieldId</c> and makes
419 /// an Ajax when its contents have changed.
420 /// </summary>
421 /// <param name="fieldId">Form field to be observed</param>
422 /// <param name="frequency">The frequency (in seconds) at which changes to
423 /// this field will be detected. (required)</param>
424 /// <param name="url">url for the action to call
425 /// when the field has changed (required)</param>
426 /// <param name="options">the options for the Ajax invocation</param>
427 /// <returns>javascript that activates the observer</returns>
428 public String ObserveField(String fieldId, int frequency, String url, IDictionary options)
430 options["url"] = url;
431 options["frequency"] = frequency;
432 return BuildObserver("Form.Element.Observer", fieldId, options);
435 /// <summary>
436 /// Observes the field with the DOM ID specified by <c>field</c> and makes
437 /// an Ajax call when its contents changes.
438 /// <para>
439 /// The following entries must exist in the dictionary:
440 /// </para>
441 /// <list type="bullet">
442 /// <item>
443 /// <term>field</term>
444 /// <description>The DOM field to be observed</description>
445 /// </item>
446 /// <item>
447 /// <term>url</term>
448 /// <description>url to to call when the field has changed</description>
449 /// </item>
450 /// <item>
451 /// <term>frequency</term>
452 /// <description>The frequency (in seconds) at which changes to this field will be detected</description>
453 /// </item>
454 /// </list>
455 /// <para>
456 /// The following are optional entries:
457 /// </para>
458 /// <list type="bullet">
459 /// <item>
460 /// <term>update</term>
461 /// <description>Specifies the DOM ID of the element whose
462 /// innerHTML should be updated with the
463 /// XMLHttpRequest response text</description>
464 /// </item>
465 /// <item>
466 /// <term>with</term>
467 /// <description>A Javascript expression specifying the parameters
468 /// for the XMLHttpRequest. This defaults to 'value', which in the
469 /// evaluated context refers to the new field value</description>
470 /// </item>
471 /// </list>
472 /// </summary>
473 /// <param name="options">the options for the Ajax invocation</param>
474 /// <returns>javascript that activates the observer</returns>
475 public String ObserveField(IDictionary options)
477 return BuildObserver("Form.Element.Observer", (String) options["field"], options);
480 #endregion
482 #region ObserveForm
484 /// <summary>
485 /// Like <see cref="ObserveField(IDictionary)"/>, but operates on an entire form identified by the
486 /// DOM ID <c>formId</c>. options are the same as <see cref="ObserveField(IDictionary)"/>, except
487 /// the default value of the <tt>:with</tt> option evaluates to the
488 /// serialized (request String) value of the form.
489 /// Works like the <see cref="ObserveField(IDictionary)"/>, but operates on an entire form identified by the
490 /// DOM ID <c>formId</c>. Options are the same as <see cref="ObserveField(IDictionary)"/>, except
491 /// the default value of the <c>with</c> option evaluates to the
492 /// serialized (request String) value of the entire form.
493 /// </summary>
494 /// <param name="formId">Form to be observed</param>
495 /// <param name="frequency">The frequency (in seconds) at which changes to
496 /// this field will be detected. (required)</param>
497 /// <param name="url">url for the action to call
498 /// when the field has changed (required)</param>
499 /// <param name="idOfElementToBeUpdated"> Specifies the DOM ID of the element whose
500 /// innerHTML should be updated with the
501 /// XMLHttpRequest response text.</param>
502 /// <param name="with">A Javascript expression specifying the
503 /// parameters for the XMLHttpRequest. This defaults
504 /// to 'value', which in the evaluated context
505 /// refers to the new field value.</param>
506 /// <returns>javascript that activates the observer</returns>
507 public String ObserveForm(String formId, int frequency, String url, String idOfElementToBeUpdated, object with)
509 IDictionary options = new HybridDictionary();
510 options["frequency"] = frequency;
511 options["url"] = url;
513 if (idOfElementToBeUpdated != null && idOfElementToBeUpdated.Length > 0) options["update"] = idOfElementToBeUpdated;
514 if (with != null) options["with"] = ProcessWith(with);
516 return ObserveForm(formId, options);
519 /// <summary>
520 /// Like <see cref="ObserveField(IDictionary)"/>, but operates on an entire form identified by the
521 /// DOM ID <c>formId</c>. options are the same as <see cref="ObserveField(IDictionary)"/>, except
522 /// the default value of the <c>with</c> option evaluates to the
523 /// serialized (request String) value of the entire form.
524 /// </summary>
525 /// <param name="formId">Form to be observed</param>
526 /// <param name="options">the options for the Ajax invocation</param>
527 /// <returns>javascript that activates the observer</returns>
528 public String ObserveForm(String formId, IDictionary options)
530 return BuildObserver("Form.Observer", formId, options);
533 /// <summary>
534 /// Observes all elements within a form with the DOM
535 /// ID specified by <c>form</c> and makes
536 /// an Ajax call when its contents changes.
537 /// <para>
538 /// The following entries must exist in the dictionary:
539 /// </para>
540 /// <list type="bullet">
541 /// <item>
542 /// <term>form</term>
543 /// <description>The form element id</description>
544 /// </item>
545 /// <item>
546 /// <term>url</term>
547 /// <description>url to to call when the field has changed</description>
548 /// </item>
549 /// <item>
550 /// <term>frequency</term>
551 /// <description>The frequency (in seconds) at which changes to this field will be detected</description>
552 /// </item>
553 /// </list>
554 /// <para>
555 /// The following are optional entries:
556 /// </para>
557 /// <list type="bullet">
558 /// <item>
559 /// <term>update</term>
560 /// <description>Specifies the DOM ID of the element whose
561 /// innerHTML should be updated with the
562 /// XMLHttpRequest response text</description>
563 /// </item>
564 /// <item>
565 /// <term>with</term>
566 /// <description>A Javascript expression specifying the parameters
567 /// for the XMLHttpRequest. This defaults to 'value', which in the
568 /// evaluated context refers to the new field value</description>
569 /// </item>
570 /// </list>
571 /// </summary>
572 /// <param name="options">the options for the Ajax invocation</param>
573 /// <returns>javascript that activates the observer</returns>
574 public String ObserveForm(IDictionary options)
576 String formId = (String) options["form"];
578 if (!options.Contains("with"))
580 options["with"] = "Form.serialize(" + formId + ")";
583 return BuildObserver("Form.Observer", formId, options);
586 #endregion
588 #region Periodically Call
590 /// <summary>
591 /// Periodically invokes the specified <c>url</c>. You can use the options to
592 /// override the default <c>frequency</c> (defaults to 10 seconds).
593 /// </summary>
594 /// <param name="options">the options for the Ajax invocation</param>
595 /// <returns>javascript that activates the timer</returns>
596 public String PeriodicallyCallRemote(IDictionary options)
598 String url = (String) options["url"];
600 options.Remove("url");
602 return PeriodicallyCallRemote(url, options);
605 /// <summary>
606 /// Periodically invokes the specified <c>url</c>. You can use the options to
607 /// override the default <c>frequency</c> (defaults to 10 seconds).
608 /// </summary>
609 /// <param name="options">the options for the Ajax invocation</param>
610 /// <param name="url">url to be invoked periodically</param>
611 /// <returns>javascript that activates the timer</returns>
612 public String PeriodicallyCallRemote(String url, IDictionary options)
614 if (options == null)
616 options = new HybridDictionary();
619 if (!options.Contains("frequency"))
621 options["frequency"] = "10";
624 String code = String.Format("new PeriodicalExecuter(function() {{ {0} }}, {1} )",
625 BuildRemoteFunction(url, options), options["frequency"]);
627 return String.Format( "<script>{0}</script>", code );
630 #endregion
632 #region AutoCompletion
634 /// <summary>
635 /// Rendes a input field with Google style autocomplete enabled.
636 /// The specified <c>url</c> is used to gather the contents
637 /// for the auto complete panel, so
638 /// and your action should return filtered and sorted results.
639 /// <para>
640 /// The following entries must exist in the options:
641 /// </para>
642 /// <list type="bullet">
643 /// <item>
644 /// <term>input</term>
645 /// <description>The text input element id</description>
646 /// </item>
647 /// <item>
648 /// <term>url</term>
649 /// <description>url to to call when the field has changed</description>
650 /// </item>
651 /// </list>
652 /// </summary>
653 /// <remarks>
654 /// it is assumed that the url invoked returns an unordered list.
655 /// </remarks>
656 /// <param name="options">the options for the Ajax invocation</param>
657 /// <param name="tagAttributes">attributes for the input html element</param>
658 /// <returns>javascript that activates the timer</returns>
659 public String InputTextWithAutoCompletion(IDictionary options, IDictionary tagAttributes)
661 String input = (String) options["input"];
662 String url = (String) options["url"];
664 options.Remove("input");
665 options.Remove("url");
667 return InputTextWithAutoCompletion(input, url, tagAttributes, options);
670 /// <summary>
671 /// Rendes a input field with Google style autocomplete enabled.
672 /// The specified url is used to gather the contents for the auto complete panel, so
673 /// and your action should return filtered and sorted results.
674 /// <seealso cref="AutoCompleteInputText"/>
675 /// </summary>
676 /// <remarks>
677 /// it is assumed that the url invoked returns an unordered list.
678 /// </remarks>
679 /// <param name="inputName">input element id</param>
680 /// <param name="url">url used to gather results</param>
681 /// <param name="tagAttributes">attributes for the input element</param>
682 /// <param name="completionOptions">options for the autocomplete</param>
683 /// <returns></returns>
684 public String InputTextWithAutoCompletion(String inputName, String url, IDictionary tagAttributes, IDictionary completionOptions)
686 StringBuilder sb = new StringBuilder();
688 sb.AppendFormat( "<input type=\"text\" autocomplete=\"off\" name=\"{0}\" id=\"{0}\" {1}/>",
689 inputName, GetAttributes(tagAttributes) );
691 sb.AppendFormat( "<div id=\"{0}\" class=\"auto_complete\"></div>", inputName + "autocomplete" );
693 sb.Append( AutoCompleteInputText( inputName, url, completionOptions ) );
695 return sb.ToString();
698 /// <summary>
699 /// Generates an javascript block enabling
700 /// auto completion for the specified input text id (<c>elementId</c>).
701 /// You can specify the element to be updated using the options
702 /// dictionary (key <c>update</c>), if you don't we assume
703 /// <c>elementId+autocomplete</c>.
704 /// </summary>
705 /// <remarks>
706 /// it is assumed that the url invoked returns an unordered list.
707 /// </remarks>
708 /// <param name="elementId">The element id (input type=text)</param>
709 /// <param name="url">The url to be invoked returning results</param>
710 /// <param name="options">the options for the Ajax invocation</param>
711 /// <returns></returns>
712 public String AutoCompleteInputText(String elementId, String url, IDictionary options)
714 if (options == null)
716 options = new HybridDictionary();
719 StringBuilder sb = new StringBuilder();
721 String update = (String) options["update"];
723 if (update == null)
725 update = elementId + "autocomplete";
728 sb.Append("<script type=\"text/javascript\">");
730 sb.AppendFormat( "new Ajax.Autocompleter('{0}', '{1}', '{2}'", elementId, update, url );
732 if (options.Contains("tokens"))
734 String[] tokens = options["tokens"].ToString().Split('|');
736 if (tokens.Length == 0)
738 options.Remove("tokens");
740 else if (tokens.Length == 1)
742 options["tokens"] = tokens[0];
744 else
746 StringBuilder content = new StringBuilder("new Array(");
748 foreach(String tok in tokens)
750 content.Append('\'').Append(tok).Append("\',");
753 if(tokens.Length > 0)
754 content.Remove( content.Length - 1, 1); // removing extra comma
756 content.Append(')');
758 options["tokens"] = content.ToString();
762 if (options.Contains("indicator"))
764 options["indicator"] = String.Format( "'{0}'", options["indicator"] );
767 sb.Append( "," );
769 sb.Append( JavascriptOptions(options) );
771 sb.Append( ")" );
773 sb.Append("</script>");
775 return sb.ToString();
778 #endregion
780 #region Supporting methods
782 /// <summary>
783 /// Returns a function that makes a remote invocation,
784 /// using the supplied parameters
785 /// </summary>
786 /// <param name="url">Target url</param>
787 /// <param name="options">the options for the Ajax invocation</param>
788 /// <returns>javascript code</returns>
789 public String BuildRemoteFunction(String url, IDictionary options)
791 if (options == null)
793 options = new HybridDictionary();
796 options["url"] = url;
798 return RemoteFunction(options);
801 /// <summary>
802 /// Returns a function that makes a remote invocation,
803 /// using the supplied parameters
804 /// </summary>
805 /// <param name="options">the options for the Ajax invocation</param>
806 /// <returns>javascript code</returns>
807 public String RemoteFunction(IDictionary options)
809 IDictionary jsOptions = new HybridDictionary();
811 String javascriptOptionsString = BuildAjaxOptions(jsOptions, options);
813 StringBuilder contents = new StringBuilder();
815 bool isRequestOnly = !options.Contains("update") &&
816 !options.Contains("success") && !options.Contains("failure");
818 if (isRequestOnly)
820 contents.Append( "new Ajax.Request(" );
822 else
824 contents.Append( "new Ajax.Updater(" );
826 if (options.Contains("update"))
828 contents.AppendFormat( "'{0}', ", options["update"] );
829 options.Remove("update");
831 else
833 contents.Append("{");
835 bool commaFirst = false;
837 if (options.Contains("success"))
839 contents.AppendFormat( "success:'{0}'", options["success"] );
840 commaFirst = true;
841 options.Remove("success");
844 if (options.Contains("failure"))
846 if (commaFirst)
848 contents.Append(",");
850 contents.AppendFormat( "failure:'{0}'", options["failure"] );
851 options.Remove("failure");
854 contents.Append("}, ");
858 if (!options.Contains("url")) throw new ArgumentException("url is required");
860 contents.Append( GetUrlOption(options) );
861 contents.Append( ", " + javascriptOptionsString + ")" );
863 if (options.Contains("before"))
865 contents = new StringBuilder( String.Format("{0}; {1}", options["before"], contents) );
867 options.Remove("before");
870 if (options.Contains("after"))
872 contents = new StringBuilder( String.Format("{1}; {0}", options["after"], contents) );
874 options.Remove("after");
877 if (options.Contains("condition"))
879 String old = contents.ToString();
881 contents = new StringBuilder(
882 String.Format("if ( {0} ) {{ {1}; }}", options["condition"], old) );
884 options.Remove("condition");
887 return contents.ToString();
890 private String GetUrlOption(IDictionary options)
892 String url = (String) options["url"];
894 if (url.StartsWith("<") && url.EndsWith(">"))
896 return url.Substring(1, url.Length - 2);
899 return "'" + url + "'";
902 /// <summary>
903 /// Populates the <paramref name="jsOptions"/> by analyzing the
904 /// options set on the helper <paramref name="options"/>
905 /// </summary>
906 ///
907 /// <remarks>
908 /// The following options are analyzed
909 ///
910 /// <list type="bullet">
911 /// <item>
912 /// <term>type</term>
913 /// <description>boolean - sets the <c>asynchronous</c> </description>
914 /// </item>
915 /// <item>
916 /// <term>method</term>
917 /// <description>string - sets the <c>method</c>. Possible values are post/get </description>
918 /// </item>
919 /// <item>
920 /// <term>evalScripts</term>
921 /// <description>boolean</description>
922 /// </item>
923 /// <item>
924 /// <term>position</term>
925 /// <description>string - sets the place where content is inserted (Top, Bottom, After, Before)</description>
926 /// </item>
927 /// <item>
928 /// <term>form</term>
929 /// <description>if present, set the parameters request to send the current form serialized</description>
930 /// </item>
931 /// <item>
932 /// <term>with</term>
933 /// <description>if present, set its content as the parameters for the ajax request</description>
934 /// </item>
935 /// </list>
936 ///
937 /// </remarks>
938 ///
939 /// <param name="jsOptions">Options that will be used on the js side</param>
940 /// <param name="options">Options passed to the helper method</param>
941 /// <returns></returns>
942 protected String BuildAjaxOptions(IDictionary jsOptions, IDictionary options)
944 BuildCallbacks(jsOptions, options);
946 jsOptions["asynchronous"] = (!options.Contains("type")).ToString().ToLower(System.Globalization.CultureInfo.InvariantCulture);
948 if (options.Contains("method"))
950 jsOptions["method"] = options["method"];
953 if (options.Contains("evalScripts"))
955 jsOptions["evalScripts"] = options["evalScripts"];
957 else
959 jsOptions["evalScripts"] = "true";
962 if (options.Contains("position"))
964 jsOptions["insertion"] = String.Format("Insertion.{0}", options["position"]);
967 if (!options.Contains("with") && options.Contains("form"))
969 jsOptions["parameters"] = "Form.serialize(this)";
971 else if (options.Contains("with"))
973 jsOptions["parameters"] = ProcessWith(options["with"]);
976 return JavascriptOptions(jsOptions);
979 private string ProcessWith(object with)
981 if (with == null) return null;
983 if (with is IDictionary)
985 return SQuote(CommonUtils.BuildQueryString(ServerUtility, with as IDictionary, false));
988 return with.ToString();
991 private void BuildCallbacks(IDictionary jsOptions, IDictionary options)
993 String[] names = CallbackEnum.GetNames( typeof(CallbackEnum) );
995 foreach(String name in names)
997 if (!options.Contains(name)) continue;
999 String callbackFunctionName;
1001 String function = BuildCallbackFunction(
1002 (CallbackEnum) Enum.Parse( typeof(CallbackEnum), name, true),
1003 options[name].ToString(), out callbackFunctionName);
1005 if (function == String.Empty) return;
1007 jsOptions[callbackFunctionName] = function;
1011 /// <summary>
1012 /// Builds the callback function.
1013 /// </summary>
1014 /// <param name="callback">The callback.</param>
1015 /// <param name="code">The code.</param>
1016 /// <param name="name">The name.</param>
1017 /// <returns></returns>
1018 protected String BuildCallbackFunction(CallbackEnum callback, String code, out String name)
1020 name = String.Empty;
1022 if (callback == CallbackEnum.Uninitialized) return String.Empty;
1024 if (callback != CallbackEnum.OnFailure && callback != CallbackEnum.OnSuccess)
1026 name = "on" + callback;
1028 else if (callback == CallbackEnum.OnFailure)
1030 name = "onFailure";
1032 else if (callback == CallbackEnum.OnSuccess)
1034 name = "onSuccess";
1037 return String.Format("function(request) {{ {0} }} ", code);
1040 /// <summary>
1041 /// Builds the observer.
1042 /// </summary>
1043 /// <param name="clazz">The clazz.</param>
1044 /// <param name="name">The name.</param>
1045 /// <param name="options">The options.</param>
1046 /// <returns></returns>
1047 protected String BuildObserver(String clazz, String name, IDictionary options)
1049 if (options.Contains("update") && !options.Contains("with"))
1051 options["with"] = "'value=' + value";
1054 String call = RemoteFunction(options);
1056 StringBuilder js = new StringBuilder();
1058 js.Append( "<script type=\"text/javascript\">" );
1059 js.Append( String.Format("new {0}('{1}', {2}, function(element, value) {{ {3} }})",
1060 clazz, name, options["frequency"], call) );
1061 js.Append( "</script>" );
1063 return js.ToString();
1066 /// <summary>
1067 /// Gets the options.
1068 /// </summary>
1069 /// <param name="url">The URL.</param>
1070 /// <param name="options">The options.</param>
1071 /// <returns></returns>
1072 public IDictionary GetOptions(String url, IDictionary options)
1074 if (options == null)
1076 options = new HybridDictionary();
1079 options["form"] = true;
1080 options["url"] = url;
1082 return options;
1085 /// <summary>
1086 /// Gets the options.
1087 /// </summary>
1088 /// <param name="url">The URL.</param>
1089 /// <param name="idOfElementToBeUpdated">The id of element to be updated.</param>
1090 /// <param name="with">The with.</param>
1091 /// <param name="loading">The loading.</param>
1092 /// <param name="loaded">The loaded.</param>
1093 /// <param name="complete">The complete.</param>
1094 /// <param name="interactive">The interactive.</param>
1095 /// <returns></returns>
1096 public IDictionary GetOptions(String url, String idOfElementToBeUpdated,
1097 object with, String loading, String loaded, String complete, String interactive)
1099 IDictionary options = new HybridDictionary();
1101 options["form"] = true;
1102 options["url"] = url;
1103 // options["method"] = method;
1105 if (with != null) options["with"] = with;
1106 if (idOfElementToBeUpdated != null && idOfElementToBeUpdated.Length > 0) options["update"] = idOfElementToBeUpdated;
1107 if (loading != null && loading.Length > 0) options["Loading"] = loading;
1108 if (loaded != null && loaded.Length > 0) options["Loaded"] = loaded;
1109 if (complete != null && complete.Length > 0) options["Complete"] = complete;
1110 if (interactive != null && interactive.Length > 0) options["Interactive"] = interactive;
1112 return options;
1115 #endregion