- Fixed MR-84
[castle.git] / MonoRail / Castle.MonoRail.Framework / Helpers / AjaxHelper.cs
blobd083cbeec263bf42a460212ea0061d1bbb9895cc
1 // Copyright 2004-2007 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 IServiceEnabledComponent implementation
84 /// <summary>
85 /// Invoked by the framework in order to give a chance to
86 /// obtain other services
87 /// </summary>
88 /// <param name="provider">The service proviver</param>
89 public void Service(IServiceProvider provider)
91 ajaxProxyGenerator = (IAjaxProxyGenerator) provider.GetService(typeof(IAjaxProxyGenerator));
94 #endregion
96 #region Scripts
98 /// <summary>
99 /// Renders a Javascript library inside a single script tag.
100 /// </summary>
101 public String InstallScripts()
103 return RenderScriptBlockToSource("/MonoRail/Files/AjaxScripts");
106 /// <summary>
107 /// Renders a Javascript library inside a single script tag.
108 /// </summary>
109 [Obsolete("Please use the preferred InstallScripts function.")]
110 public String GetJavascriptFunctions()
112 return InstallScripts();
115 #endregion
117 #region GenerateJSProxy overloads
119 /// <summary>
120 /// Generates an AJAX JavaScript proxy for the current controller.
121 /// </summary>
122 /// <param name="proxyName">Name of the javascript proxy object</param>
123 public String GenerateJSProxy(string proxyName)
125 return GenerateJSProxy(proxyName, Controller.AreaName, Controller.Name);
128 /// <summary>
129 /// Generates an AJAX JavaScript proxy for a given controller.
130 /// </summary>
131 /// <param name="proxyName">Name of the javascript proxy object</param>
132 /// <param name="controller">Controller which will be target of the proxy</param>
133 public String GenerateJSProxy(string proxyName, string controller)
135 return GenerateJSProxy(proxyName, String.Empty, controller);
138 /// <summary>
139 /// Generates an AJAX JavaScript proxy for a given controller.
140 /// </summary>
141 /// <param name="proxyName">Name of the javascript proxy object</param>
142 /// <param name="controller">Controller which will be target of the proxy</param>
143 /// <param name="area">area which the controller belongs to</param>
144 public String GenerateJSProxy(string proxyName, string area, string controller)
146 return ajaxProxyGenerator.GenerateJSProxy(CurrentContext, proxyName, area, controller);
149 #endregion
151 #region LinkToFunction
153 /// <summary>
154 /// Returns a link that will trigger a javascript function using the
155 /// onclick handler and return false after the fact.
156 /// <code>
157 /// &lt;a href="javascript:void(0);" onclick="functionCodeOrName; return false"&gt;innerContent&lt;/a&gt;
158 /// </code>
159 /// </summary>
160 /// <param name="innerContent">Link content</param>
161 /// <param name="functionCodeOrName">Function definition</param>
162 /// <param name="attributes">Attributes to be applied to the html element</param>
163 /// <returns></returns>
164 public String LinkToFunction(String innerContent, String functionCodeOrName, IDictionary attributes)
166 String htmlAtt = GetAttributes(attributes);
168 return String.Format("<a href=\"javascript:void(0);\" {2} onclick=\"{0}; return false;\" >{1}</a>", functionCodeOrName, innerContent, htmlAtt );
171 /// <summary>
172 /// Returns a link that will trigger a javascript function using the
173 /// onclick handler and return false after the fact.
174 /// <code>
175 /// &lt;a href="javascript:void(0);" onclick="confirm('question') { functionCodeOrName}; return false"&gt;innerContent&lt;/a&gt;
176 /// </code>
177 /// </summary>
178 /// <param name="innerContent">Link content</param>
179 /// <param name="functionCodeOrName">Function definition</param>
180 /// <param name="confirm">Confirm question</param>
181 /// <param name="attributes">Attributes to be applied to the html element</param>
182 /// <returns></returns>
183 public String LinkToFunction(String innerContent, String functionCodeOrName, string confirm, IDictionary attributes)
185 String htmlAtt = GetAttributes(attributes);
187 return String.Format("<a href=\"javascript:void(0);\" {2} onclick=\"if(confirm('" + confirm + "')){{{0}}};return false;\" >{1}</a>", functionCodeOrName, innerContent, htmlAtt);
190 /// <summary>
191 /// Returns a link that will trigger a javascript function using the
192 /// onclick handler and return false after the fact.
193 /// </summary>
194 /// <param name="innerContent">Link content</param>
195 /// <param name="functionCodeOrName">Function definition</param>
196 /// <returns></returns>
197 public String LinkToFunction(String innerContent, String functionCodeOrName)
199 return LinkToFunction(innerContent, functionCodeOrName, new Hashtable());
202 /// <summary>
203 /// Returns a link that will trigger a javascript function using the
204 /// onclick handler and return false after the fact.
205 /// </summary>
206 /// <param name="innerContent">Link content</param>
207 /// <param name="functionCodeOrName">Function definition</param>
208 /// <param name="confirm">Confirm question</param>
209 /// <returns></returns>
210 public String LinkToFunction(String innerContent, String functionCodeOrName, String confirm)
212 return LinkToFunction(innerContent, functionCodeOrName, confirm, null);
215 #endregion
217 #region ButtonToFunction
219 /// <summary>
220 /// Returns a button that will trigger a javascript function using the
221 /// onclick handler and return false after the fact.
222 /// </summary>
223 /// <param name="innerContent">Button legend</param>
224 /// <param name="functionCodeOrName">Function definition or name</param>
225 /// <param name="attributes">Attributes to be applied to the input html element</param>
226 /// <returns></returns>
227 public String ButtonToFunction(String innerContent, String functionCodeOrName, IDictionary attributes)
229 String htmlAtt = GetAttributes(attributes);
231 return String.Format("<input type=\"button\" {2} onclick=\"{0}; return false;\" value=\"{1}\" />",
232 functionCodeOrName, innerContent, htmlAtt);
235 /// <summary>
236 /// Returns a button that will trigger a javascript function using the
237 /// onclick handler and return false after the fact.
238 /// </summary>
239 /// <param name="innerContent">Button legend</param>
240 /// <param name="functionCodeOrName">Function definition or name</param>
241 /// <returns></returns>
242 public String ButtonToFunction(String innerContent, String functionCodeOrName)
244 return ButtonToFunction(innerContent, functionCodeOrName, null);
247 #endregion
249 #region ButtonToRemote
251 /// <summary>
252 /// Creates a button that if clicked will fire an Ajax invocation.
253 /// </summary>
254 /// <param name="innerContent">Button legend</param>
255 /// <param name="url">The URL of the Ajax action</param>
256 /// <param name="options">the options for the Ajax invocation</param>
257 /// <returns>The handcrafted input</returns>
258 public String ButtonToRemote(String innerContent, String url, IDictionary options)
260 return ButtonToFunction(innerContent, BuildRemoteFunction(url, options));
263 /// <summary>
264 /// Creates a button that if clicked will fire an Ajax invocation.
265 /// </summary>
266 /// <param name="innerContent">Button legend</param>
267 /// <param name="url">the target url</param>
268 /// <param name="options">the options for the Ajax invocation</param>
269 /// <param name="htmloptions">Attributes to be applied to the html element</param>
270 /// <returns>The handcrafted input</returns>
271 public String ButtonToRemote(String innerContent, String url, IDictionary options, IDictionary htmloptions)
273 return ButtonToFunction(innerContent, BuildRemoteFunction(url, options), htmloptions);
276 #endregion
278 #region LinkToRemote
280 /// <summary>
281 /// Returns a link to a remote action defined by <c>options["url"]</c>
282 /// that is called in the background using
283 /// XMLHttpRequest. The result of that request can then be inserted into a
284 /// DOM object whose id can be specified with <c>options["update"]</c>.
285 /// Usually, the result would be a partial prepared by the controller
286 /// </summary>
287 /// <param name="innerContent">Link content</param>
288 /// <param name="url">Target url</param>
289 /// <param name="options">the options for the Ajax invocation</param>
290 /// <returns>The handcrafted element</returns>
291 public String LinkToRemote(String innerContent, String url, IDictionary options)
293 return LinkToFunction(innerContent, BuildRemoteFunction(url, options));
296 /// <summary>
297 /// Returns a link to a remote action defined by <c>options["url"]</c>
298 /// that is called in the background using
299 /// XMLHttpRequest. The result of that request can then be inserted into a
300 /// DOM object whose id can be specified with <c>options["update"]</c>.
301 /// Usually, the result would be a partial prepared by the controller
302 /// </summary>
303 /// <param name="innerContent">Link content</param>
304 /// <param name="confirm">the confirm question</param>
305 /// <param name="url">Target url</param>
306 /// <param name="options">the options for the Ajax invocation</param>
307 /// <returns>The handcrafted element</returns>
308 public String LinkToRemote(String innerContent, String confirm, String url, IDictionary options)
310 return LinkToFunction(innerContent, BuildRemoteFunction(url, options), confirm, null);
313 /// <summary>
314 /// Returns a link to a remote action defined by <c>options["url"]</c>
315 /// that is called in the background using
316 /// XMLHttpRequest. The result of that request can then be inserted into a
317 /// DOM object whose id can be specified with <c>options["update"]</c>.
318 /// Usually, the result would be a partial prepared by the controller
319 /// </summary>
320 /// <param name="innerContent">Link content</param>
321 /// <param name="url">Target url</param>
322 /// <param name="options">the options for the Ajax invocation</param>
323 /// <param name="htmloptions">Attributes to be applied to the html element</param>
324 /// <returns>The handcrafted element</returns>
325 public String LinkToRemote(String innerContent, String url, IDictionary options, IDictionary htmloptions)
327 return LinkToFunction(innerContent, BuildRemoteFunction(url, options), htmloptions);
330 #endregion
332 #region BuildFormRemoteTag
334 /// <summary>
335 /// Returns a form tag that will submit using XMLHttpRequest
336 /// in the background instead of the regular
337 /// reloading POST arrangement. Even though it is
338 /// using Javascript to serialize the form elements, the form submission
339 /// will work just like a regular submission as viewed by the
340 /// receiving side (all elements available).
341 /// </summary>
342 /// <param name="url">Target url</param>
343 /// <param name="options">the options for the Ajax invocation</param>
344 /// <returns>The handcrafted element</returns>
345 public String BuildFormRemoteTag(String url, IDictionary options)
347 return BuildFormRemoteTag( GetOptions(url, options) );
350 /// <summary>
351 /// Returns a form tag that will submit using XMLHttpRequest
352 /// in the background instead of the regular
353 /// reloading POST arrangement. Even though it is
354 /// using Javascript to serialize the form elements, the form submission
355 /// will work just like a regular submission as viewed by the
356 /// receiving side (all elements available).
357 /// </summary>
358 /// <param name="options">the options for the Ajax invocation</param>
359 /// <returns>The handcrafted element</returns>
360 public String BuildFormRemoteTag(IDictionary options)
362 options["form"] = true;
364 String remoteFunc = RemoteFunction(options);
366 String formId = options.Contains("formId") ? ("id=\"" + (String) options["formId"] + "\"") : String.Empty;
368 return String.Format("<form {1} onsubmit=\"{0}; return false;\" enctype=\"multipart/form-data\" action=\"{2}\" method=\"post\" >", remoteFunc, formId, options["url"]);
371 #endregion
373 #region ObserveField
375 /// <summary>
376 /// Observes the field with the DOM ID specified by <c>fieldId</c> and makes
377 /// an Ajax when its contents have changed.
378 /// </summary>
379 /// <param name="fieldId">Form field to be observed</param>
380 /// <param name="frequency">The frequency (in seconds) at which changes to
381 /// this field will be detected. (required)</param>
382 /// <param name="url">url for the action to call
383 /// when the field has changed (required)</param>
384 /// <param name="idOfElementToBeUpdated"> Specifies the DOM ID of the element whose
385 /// innerHTML should be updated with the
386 /// XMLHttpRequest response text.</param>
387 /// <param name="with">A Javascript expression specifying the
388 /// parameters for the XMLHttpRequest. This defaults
389 /// to 'value', which in the evaluated context
390 /// refers to the new field value.</param>
391 /// <returns>javascript that activates the observer</returns>
392 public String ObserveField(String fieldId, int frequency, String url, String idOfElementToBeUpdated, String with)
394 IDictionary options = new HybridDictionary();
395 options["frequency"] = frequency;
396 options["url"] = url;
398 if (idOfElementToBeUpdated != null) options["update"] = idOfElementToBeUpdated;
399 if (with != null) options["with"] = with;
401 return BuildObserver("Form.Element.Observer", fieldId, options);
404 /// <summary>
405 /// Observes the field with the DOM ID specified by <c>fieldId</c> and makes
406 /// an Ajax when its contents have changed.
407 /// </summary>
408 /// <param name="fieldId">Form field to be observed</param>
409 /// <param name="frequency">The frequency (in seconds) at which changes to
410 /// this field will be detected. (required)</param>
411 /// <param name="url">url for the action to call
412 /// when the field has changed (required)</param>
413 /// <param name="options">the options for the Ajax invocation</param>
414 /// <returns>javascript that activates the observer</returns>
415 public String ObserveField(String fieldId, int frequency, String url, IDictionary options)
417 options["url"] = url;
418 options["frequency"] = frequency;
419 return BuildObserver("Form.Element.Observer", fieldId, options);
422 /// <summary>
423 /// Observes the field with the DOM ID specified by <c>field</c> and makes
424 /// an Ajax call when its contents changes.
425 /// <para>
426 /// The following entries must exist in the dictionary:
427 /// </para>
428 /// <list type="bullet">
429 /// <item>
430 /// <term>field</term>
431 /// <description>The DOM field to be observed</description>
432 /// </item>
433 /// <item>
434 /// <term>url</term>
435 /// <description>url to to call when the field has changed</description>
436 /// </item>
437 /// <item>
438 /// <term>frequency</term>
439 /// <description>The frequency (in seconds) at which changes to this field will be detected</description>
440 /// </item>
441 /// </list>
442 /// <para>
443 /// The following are optional entries:
444 /// </para>
445 /// <list type="bullet">
446 /// <item>
447 /// <term>update</term>
448 /// <description>Specifies the DOM ID of the element whose
449 /// innerHTML should be updated with the
450 /// XMLHttpRequest response text</description>
451 /// </item>
452 /// <item>
453 /// <term>with</term>
454 /// <description>A Javascript expression specifying the parameters
455 /// for the XMLHttpRequest. This defaults to 'value', which in the
456 /// evaluated context refers to the new field value</description>
457 /// </item>
458 /// </list>
459 /// </summary>
460 /// <param name="options">the options for the Ajax invocation</param>
461 /// <returns>javascript that activates the observer</returns>
462 public String ObserveField(IDictionary options)
464 return BuildObserver("Form.Element.Observer", (String) options["field"], options);
467 #endregion
469 #region ObserveForm
471 /// <summary>
472 /// Like <see cref="ObserveField(IDictionary)"/>, but operates on an entire form identified by the
473 /// DOM ID <c>formId</c>. options are the same as <see cref="ObserveField(IDictionary)"/>, except
474 /// the default value of the <tt>:with</tt> option evaluates to the
475 /// serialized (request String) value of the form.
476 /// Works like the <see cref="ObserveField(IDictionary)"/>, but operates on an entire form identified by the
477 /// DOM ID <c>formId</c>. Options are the same as <see cref="ObserveField(IDictionary)"/>, except
478 /// the default value of the <c>with</c> option evaluates to the
479 /// serialized (request String) value of the entire form.
480 /// </summary>
481 /// <param name="formId">Form to be observed</param>
482 /// <param name="frequency">The frequency (in seconds) at which changes to
483 /// this field will be detected. (required)</param>
484 /// <param name="url">url for the action to call
485 /// when the field has changed (required)</param>
486 /// <param name="idOfElementToBeUpdated"> Specifies the DOM ID of the element whose
487 /// innerHTML should be updated with the
488 /// XMLHttpRequest response text.</param>
489 /// <param name="with">A Javascript expression specifying the
490 /// parameters for the XMLHttpRequest. This defaults
491 /// to 'value', which in the evaluated context
492 /// refers to the new field value.</param>
493 /// <returns>javascript that activates the observer</returns>
494 public String ObserveForm(String formId, int frequency, String url, String idOfElementToBeUpdated, object with)
496 IDictionary options = new HybridDictionary();
497 options["frequency"] = frequency;
498 options["url"] = url;
500 if (idOfElementToBeUpdated != null && idOfElementToBeUpdated.Length > 0) options["update"] = idOfElementToBeUpdated;
501 if (with != null) options["with"] = ProcessWith(with);
503 return ObserveForm(formId, options);
506 /// <summary>
507 /// Like <see cref="ObserveField(IDictionary)"/>, but operates on an entire form identified by the
508 /// DOM ID <c>formId</c>. options are the same as <see cref="ObserveField(IDictionary)"/>, except
509 /// the default value of the <c>with</c> option evaluates to the
510 /// serialized (request String) value of the entire form.
511 /// </summary>
512 /// <param name="formId">Form to be observed</param>
513 /// <param name="options">the options for the Ajax invocation</param>
514 /// <returns>javascript that activates the observer</returns>
515 public String ObserveForm(String formId, IDictionary options)
517 return BuildObserver("Form.Observer", formId, options);
520 /// <summary>
521 /// Observes all elements within a form with the DOM
522 /// ID specified by <c>form</c> and makes
523 /// an Ajax call when its contents changes.
524 /// <para>
525 /// The following entries must exist in the dictionary:
526 /// </para>
527 /// <list type="bullet">
528 /// <item>
529 /// <term>form</term>
530 /// <description>The form element id</description>
531 /// </item>
532 /// <item>
533 /// <term>url</term>
534 /// <description>url to to call when the field has changed</description>
535 /// </item>
536 /// <item>
537 /// <term>frequency</term>
538 /// <description>The frequency (in seconds) at which changes to this field will be detected</description>
539 /// </item>
540 /// </list>
541 /// <para>
542 /// The following are optional entries:
543 /// </para>
544 /// <list type="bullet">
545 /// <item>
546 /// <term>update</term>
547 /// <description>Specifies the DOM ID of the element whose
548 /// innerHTML should be updated with the
549 /// XMLHttpRequest response text</description>
550 /// </item>
551 /// <item>
552 /// <term>with</term>
553 /// <description>A Javascript expression specifying the parameters
554 /// for the XMLHttpRequest. This defaults to 'value', which in the
555 /// evaluated context refers to the new field value</description>
556 /// </item>
557 /// </list>
558 /// </summary>
559 /// <param name="options">the options for the Ajax invocation</param>
560 /// <returns>javascript that activates the observer</returns>
561 public String ObserveForm(IDictionary options)
563 String formId = (String) options["form"];
565 if (!options.Contains("with"))
567 options["with"] = "Form.serialize(" + formId + ")";
570 return BuildObserver("Form.Observer", formId, options);
573 #endregion
575 #region Periodically Call
577 /// <summary>
578 /// Periodically invokes the specified <c>url</c>. You can use the options to
579 /// override the default <c>frequency</c> (defaults to 10 seconds).
580 /// </summary>
581 /// <param name="options">the options for the Ajax invocation</param>
582 /// <returns>javascript that activates the timer</returns>
583 public String PeriodicallyCallRemote(IDictionary options)
585 String url = (String) options["url"];
587 options.Remove("url");
589 return PeriodicallyCallRemote(url, options);
592 /// <summary>
593 /// Periodically invokes the specified <c>url</c>. You can use the options to
594 /// override the default <c>frequency</c> (defaults to 10 seconds).
595 /// </summary>
596 /// <param name="options">the options for the Ajax invocation</param>
597 /// <param name="url">url to be invoked periodically</param>
598 /// <returns>javascript that activates the timer</returns>
599 public String PeriodicallyCallRemote(String url, IDictionary options)
601 if (options == null)
603 options = new HybridDictionary();
606 if (!options.Contains("frequency"))
608 options["frequency"] = "10";
611 String code = String.Format("new PeriodicalExecuter(function() {{ {0} }}, {1} )",
612 BuildRemoteFunction(url, options), options["frequency"]);
614 return String.Format( "<script>{0}</script>", code );
617 #endregion
619 #region AutoCompletion
621 /// <summary>
622 /// Rendes a input field with Google style autocomplete enabled.
623 /// The specified <c>url</c> is used to gather the contents
624 /// for the auto complete panel, so
625 /// and your action should return filtered and sorted results.
626 /// <para>
627 /// The following entries must exist in the options:
628 /// </para>
629 /// <list type="bullet">
630 /// <item>
631 /// <term>input</term>
632 /// <description>The text input element id</description>
633 /// </item>
634 /// <item>
635 /// <term>url</term>
636 /// <description>url to to call when the field has changed</description>
637 /// </item>
638 /// </list>
639 /// </summary>
640 /// <remarks>
641 /// it is assumed that the url invoked returns an unordered list.
642 /// </remarks>
643 /// <param name="options">the options for the Ajax invocation</param>
644 /// <param name="tagAttributes">attributes for the input html element</param>
645 /// <returns>javascript that activates the timer</returns>
646 public String InputTextWithAutoCompletion(IDictionary options, IDictionary tagAttributes)
648 String input = (String) options["input"];
649 String url = (String) options["url"];
651 options.Remove("input");
652 options.Remove("url");
654 return InputTextWithAutoCompletion(input, url, tagAttributes, options);
657 /// <summary>
658 /// Rendes a input field with Google style autocomplete enabled.
659 /// The specified url is used to gather the contents for the auto complete panel, so
660 /// and your action should return filtered and sorted results.
661 /// <seealso cref="AutoCompleteInputText"/>
662 /// </summary>
663 /// <remarks>
664 /// it is assumed that the url invoked returns an unordered list.
665 /// </remarks>
666 /// <param name="inputName">input element id</param>
667 /// <param name="url">url used to gather results</param>
668 /// <param name="tagAttributes">attributes for the input element</param>
669 /// <param name="completionOptions">options for the autocomplete</param>
670 /// <returns></returns>
671 public String InputTextWithAutoCompletion(String inputName, String url, IDictionary tagAttributes, IDictionary completionOptions)
673 StringBuilder sb = new StringBuilder();
675 sb.AppendFormat( "<input type=\"text\" autocomplete=\"off\" name=\"{0}\" id=\"{0}\" {1}/>",
676 inputName, GetAttributes(tagAttributes) );
678 sb.AppendFormat( "<div id=\"{0}\" class=\"auto_complete\"></div>", inputName + "autocomplete" );
680 sb.Append( AutoCompleteInputText( inputName, url, completionOptions ) );
682 return sb.ToString();
685 /// <summary>
686 /// Generates an javascript block enabling
687 /// auto completion for the specified input text id (<c>elementId</c>).
688 /// You can specify the element to be updated using the options
689 /// dictionary (key <c>update</c>), if you don't we assume
690 /// <c>elementId+autocomplete</c>.
691 /// </summary>
692 /// <remarks>
693 /// it is assumed that the url invoked returns an unordered list.
694 /// </remarks>
695 /// <param name="elementId">The element id (input type=text)</param>
696 /// <param name="url">The url to be invoked returning results</param>
697 /// <param name="options">the options for the Ajax invocation</param>
698 /// <returns></returns>
699 public String AutoCompleteInputText(String elementId, String url, IDictionary options)
701 if (options == null)
703 options = new HybridDictionary();
706 StringBuilder sb = new StringBuilder();
708 String update = (String) options["update"];
710 if (update == null)
712 update = elementId + "autocomplete";
715 sb.Append("<script type=\"text/javascript\">");
717 sb.AppendFormat( "new Ajax.Autocompleter('{0}', '{1}', '{2}'", elementId, update, url );
719 if (options.Contains("tokens"))
721 String[] tokens = options["tokens"].ToString().Split('|');
723 if (tokens.Length == 0)
725 options.Remove("tokens");
727 else if (tokens.Length == 1)
729 options["tokens"] = tokens[0];
731 else
733 StringBuilder content = new StringBuilder("new Array(");
735 foreach(String tok in tokens)
737 content.Append('\'').Append(tok).Append("\',");
740 if(tokens.Length > 0)
741 content.Remove( content.Length - 1, 1); // removing extra comma
743 content.Append(')');
745 options["tokens"] = content.ToString();
749 if (options.Contains("indicator"))
751 options["indicator"] = String.Format( "'{0}'", options["indicator"] );
754 sb.Append( "," );
756 sb.Append( JavascriptOptions(options) );
758 sb.Append( ")" );
760 sb.Append("</script>");
762 return sb.ToString();
765 #endregion
767 #region Supporting methods
769 /// <summary>
770 /// Returns a function that makes a remote invocation,
771 /// using the supplied parameters
772 /// </summary>
773 /// <param name="url">Target url</param>
774 /// <param name="options">the options for the Ajax invocation</param>
775 /// <returns>javascript code</returns>
776 public String BuildRemoteFunction(String url, IDictionary options)
778 if (options == null)
780 options = new HybridDictionary();
783 options["url"] = url;
785 return RemoteFunction(options);
788 /// <summary>
789 /// Returns a function that makes a remote invocation,
790 /// using the supplied parameters
791 /// </summary>
792 /// <param name="options">the options for the Ajax invocation</param>
793 /// <returns>javascript code</returns>
794 public String RemoteFunction(IDictionary options)
796 IDictionary jsOptions = new HybridDictionary();
798 String javascriptOptionsString = BuildAjaxOptions(jsOptions, options);
800 StringBuilder contents = new StringBuilder();
802 bool isRequestOnly = !options.Contains("update") &&
803 !options.Contains("success") && !options.Contains("failure");
805 if (isRequestOnly)
807 contents.Append( "new Ajax.Request(" );
809 else
811 contents.Append( "new Ajax.Updater(" );
813 if (options.Contains("update"))
815 contents.AppendFormat( "'{0}', ", options["update"] );
816 options.Remove("update");
818 else
820 contents.Append("{");
822 bool commaFirst = false;
824 if (options.Contains("success"))
826 contents.AppendFormat( "success:'{0}'", options["success"] );
827 commaFirst = true;
828 options.Remove("success");
831 if (options.Contains("failure"))
833 if (commaFirst)
835 contents.Append(",");
837 contents.AppendFormat( "failure:'{0}'", options["failure"] );
838 options.Remove("failure");
841 contents.Append("}, ");
845 if (!options.Contains("url")) throw new ArgumentException("url is required");
847 contents.Append( GetUrlOption(options) );
848 contents.Append( ", " + javascriptOptionsString + ")" );
850 if (options.Contains("before"))
852 contents = new StringBuilder( String.Format("{0}; {1}", options["before"], contents) );
854 options.Remove("before");
857 if (options.Contains("after"))
859 contents = new StringBuilder( String.Format("{1}; {0}", options["after"], contents) );
861 options.Remove("after");
864 if (options.Contains("condition"))
866 String old = contents.ToString();
868 contents = new StringBuilder(
869 String.Format("if ( {0} ) {{ {1}; }}", options["condition"], old) );
871 options.Remove("condition");
874 return contents.ToString();
877 private String GetUrlOption(IDictionary options)
879 String url = (String) options["url"];
881 if (url.StartsWith("<") && url.EndsWith(">"))
883 return url.Substring(1, url.Length - 2);
886 return "'" + url + "'";
889 /// <summary>
890 /// Populates the <paramref name="jsOptions"/> by analyzing the
891 /// options set on the helper <paramref name="options"/>
892 /// </summary>
893 ///
894 /// <remarks>
895 /// The following options are analyzed
896 ///
897 /// <list type="bullet">
898 /// <item>
899 /// <term>type</term>
900 /// <description>boolean - sets the <c>asynchronous</c> </description>
901 /// </item>
902 /// <item>
903 /// <term>method</term>
904 /// <description>string - sets the <c>method</c>. Possible values are post/get </description>
905 /// </item>
906 /// <item>
907 /// <term>evalScripts</term>
908 /// <description>boolean</description>
909 /// </item>
910 /// <item>
911 /// <term>position</term>
912 /// <description>string - sets the place where content is inserted (Top, Bottom, After, Before)</description>
913 /// </item>
914 /// <item>
915 /// <term>form</term>
916 /// <description>if present, set the parameters request to send the current form serialized</description>
917 /// </item>
918 /// <item>
919 /// <term>with</term>
920 /// <description>if present, set its content as the parameters for the ajax request</description>
921 /// </item>
922 /// </list>
923 ///
924 /// </remarks>
925 ///
926 /// <param name="jsOptions">Options that will be used on the js side</param>
927 /// <param name="options">Options passed to the helper method</param>
928 /// <returns></returns>
929 protected String BuildAjaxOptions(IDictionary jsOptions, IDictionary options)
931 BuildCallbacks(jsOptions, options);
933 jsOptions["asynchronous"] = (!options.Contains("type")).ToString().ToLower(System.Globalization.CultureInfo.InvariantCulture);
935 if (options.Contains("method"))
937 jsOptions["method"] = options["method"];
940 if (options.Contains("evalScripts"))
942 jsOptions["evalScripts"] = options["evalScripts"];
944 else
946 jsOptions["evalScripts"] = "true";
949 if (options.Contains("position"))
951 jsOptions["insertion"] = String.Format("Insertion.{0}", options["position"]);
954 if (!options.Contains("with") && options.Contains("form"))
956 jsOptions["parameters"] = "Form.serialize(this)";
958 else if (options.Contains("with"))
960 jsOptions["parameters"] = ProcessWith(options["with"]);
963 return JavascriptOptions(jsOptions);
966 private string ProcessWith(object with)
968 if (with == null) return null;
970 if (with is IDictionary)
972 return SQuote(CommonUtils.BuildQueryString(ServerUtility, with as IDictionary, false));
975 return with.ToString();
978 private void BuildCallbacks(IDictionary jsOptions, IDictionary options)
980 String[] names = CallbackEnum.GetNames( typeof(CallbackEnum) );
982 foreach(String name in names)
984 if (!options.Contains(name)) continue;
986 String callbackFunctionName;
988 String function = BuildCallbackFunction(
989 (CallbackEnum) Enum.Parse( typeof(CallbackEnum), name, true),
990 options[name].ToString(), out callbackFunctionName);
992 if (function == String.Empty) return;
994 jsOptions[callbackFunctionName] = function;
998 /// <summary>
999 /// Builds the callback function.
1000 /// </summary>
1001 /// <param name="callback">The callback.</param>
1002 /// <param name="code">The code.</param>
1003 /// <param name="name">The name.</param>
1004 /// <returns></returns>
1005 protected String BuildCallbackFunction(CallbackEnum callback, String code, out String name)
1007 name = String.Empty;
1009 if (callback == CallbackEnum.Uninitialized) return String.Empty;
1011 if (callback != CallbackEnum.OnFailure && callback != CallbackEnum.OnSuccess)
1013 name = "on" + callback;
1015 else if (callback == CallbackEnum.OnFailure)
1017 name = "onFailure";
1019 else if (callback == CallbackEnum.OnSuccess)
1021 name = "onSuccess";
1024 return String.Format("function(request) {{ {0} }} ", code);
1027 /// <summary>
1028 /// Builds the observer.
1029 /// </summary>
1030 /// <param name="clazz">The clazz.</param>
1031 /// <param name="name">The name.</param>
1032 /// <param name="options">The options.</param>
1033 /// <returns></returns>
1034 protected String BuildObserver(String clazz, String name, IDictionary options)
1036 if (options.Contains("update") && !options.Contains("with"))
1038 options["with"] = "'value=' + value";
1041 String call = RemoteFunction(options);
1043 StringBuilder js = new StringBuilder();
1045 js.Append( "<script type=\"text/javascript\">" );
1046 js.Append( String.Format("new {0}('{1}', {2}, function(element, value) {{ {3} }})",
1047 clazz, name, options["frequency"], call) );
1048 js.Append( "</script>" );
1050 return js.ToString();
1053 /// <summary>
1054 /// Gets the options.
1055 /// </summary>
1056 /// <param name="url">The URL.</param>
1057 /// <param name="options">The options.</param>
1058 /// <returns></returns>
1059 public IDictionary GetOptions(String url, IDictionary options)
1061 if (options == null)
1063 options = new HybridDictionary();
1066 options["form"] = true;
1067 options["url"] = url;
1069 return options;
1072 /// <summary>
1073 /// Gets the options.
1074 /// </summary>
1075 /// <param name="url">The URL.</param>
1076 /// <param name="idOfElementToBeUpdated">The id of element to be updated.</param>
1077 /// <param name="with">The with.</param>
1078 /// <param name="loading">The loading.</param>
1079 /// <param name="loaded">The loaded.</param>
1080 /// <param name="complete">The complete.</param>
1081 /// <param name="interactive">The interactive.</param>
1082 /// <returns></returns>
1083 public IDictionary GetOptions(String url, String idOfElementToBeUpdated,
1084 object with, String loading, String loaded, String complete, String interactive)
1086 IDictionary options = new HybridDictionary();
1088 options["form"] = true;
1089 options["url"] = url;
1090 // options["method"] = method;
1092 if (with != null) options["with"] = with;
1093 if (idOfElementToBeUpdated != null && idOfElementToBeUpdated.Length > 0) options["update"] = idOfElementToBeUpdated;
1094 if (loading != null && loading.Length > 0) options["Loading"] = loading;
1095 if (loaded != null && loaded.Length > 0) options["Loaded"] = loaded;
1096 if (complete != null && complete.Length > 0) options["Complete"] = complete;
1097 if (interactive != null && interactive.Length > 0) options["Interactive"] = interactive;
1099 return options;
1102 #endregion