0001"""
0002Scriptaculous Helpers
0003
0004Provides a set of helpers for calling Scriptaculous JavaScript
0005functions, including those which create Ajax controls and visual effects.
0006
0007To be able to use these helpers, you must include the Prototype
0008JavaScript framework and the Scriptaculous JavaScript library in your
0009pages.
0010
0011The Scriptaculous helpers' behavior can be tweaked with various options.
0012See the documentation at http://script.aculo.us for more information on
0013using these helpers in your application.
0014"""
0015# Last synced with Rails copy at Revision 6057 on Feb 9th, 2007.
0016import simplejson as json
0017from prototype import *
0018from javascript import options_for_javascript, array_or_string_for_javascript
0019from prototype import AJAX_OPTIONS, javascript_tag
0020from tags import camelize
0021
0022def visual_effect(name, element_id=False, **js_options):
0023 """
0024 Returns a JavaScript snippet to be used on the Ajax callbacks for
0025 starting visual effects.
0026
0027 Example::
0028
0029 <% link_to_remote("Reload",
0030 dict(url=url(action="reload"),
0031 update="posts",
0032 complete=visual_effect('highlight', "posts", duration=0.5))) %>
0033
0034 If no element_id is given, it assumes "element" which should be a local
0035 variable in the generated JavaScript execution context. This can be
0036 used for example with drop_receiving_element::
0037
0038 <% drop_receving_element('some_element', loading=visual_effect('fade')) %>
0039
0040 This would fade the element that was dropped on the drop receiving
0041 element.
0042
0043 For toggling visual effects, you can use ``toggle_appear``, ``toggle_slide``, and
0044 ``toggle_blind`` which will alternate between appear/fade, slidedown/slideup, and
0045 blinddown/blindup respectively.
0046
0047 You can change the behaviour with various options, see
0048 http://script.aculo.us for more documentation.
0049 """
0050 element = (element_id and json.dumps(element_id)) or "element"
0051 if isinstance(js_options.get('queue'), dict):
0052 js_options['queue'] = '{%s}' % ','.join(["%s:%s" % (k, (k == 'limit' and v) or "'%s'" % v) for k,v in js_options['queue'].iteritems()])
0055 elif js_options.has_key('queue'):
0056 js_options['queue'] = "'%s'" % js_options['queue']
0057
0058
0059 if 'toggle' in name:
0060 return "Effect.toggle(%s,'%s',%s);" % (element, name.replace('toggle_',''), options_for_javascript(js_options))
0061 return "new Effect.%s(%s,%s);" % (camelize(name), element, options_for_javascript(js_options))
0062
0063def parallel_effects(*effects, **js_options):
0064 """
0065 Wraps visual effects so they occur in parallel
0066
0067 Example::
0068
0069 parallel_effects(
0070 visual_effect('highlight, 'dom_id'),
0071 visual_effect('fade', 'dom_id'),
0072 )
0073 """
0074 str_effects = [e[:e.rindex(';')] for e in effects] # Remove trailing ';'
0075 return "new Effect.Parallel([%s], %s)" % (','.join(str_effects), options_for_javascript(js_options))
0076
0077def sortable_element(element_id, **options):
0078 """
0079 Makes the element with the DOM ID specified by ``element_id`` sortable.
0080
0081 Uses drag-and-drop and makes an Ajax call whenever the sort order has
0082 changed. By default, the action called gets the serialized sortable
0083 element as parameters.
0084
0085 Example::
0086
0087 <% sortable_element("my_list", url=url(action="order")) %>
0088
0089 In the example, the server-side action gets a "my_list" array
0090 parameter containing the values of the ids of elements the
0091 sortable consists of, in the current order (like
0092 ``mylist=item1&mylist=item2``, where ``item1`` and ``item2`` are
0093 the ids of the ``<li>`` elements).
0094
0095 Note: For this to work, the sortable elements must have id
0096 attributes in the form ``string_identifier``. For example,
0097 ``item_1``. Only the identifier part of the id attribute will be
0098 serialized.
0099
0100 You can change the behaviour with various options, see
0101 http://script.aculo.us for more documentation.
0102 """
0103 return javascript_tag(sortable_element_js(element_id, **options))
0104
0105def sortable_element_js(element_id, **options):
0106 if not isinstance(element_id, basestring):
0107 raise ValueError('Argument element_id must be a string')
0108 options.setdefault('with_', "Sortable.serialize('%s')" % element_id)
0109 options.setdefault('onUpdate', "function(){%s}" % remote_function(**options))
0110 for k in options.keys():
0111 if k in AJAX_OPTIONS: del options[k]
0112
0113 for option in ['tag', 'overlap', 'constraint', 'handle']:
0114 if options.has_key(option) and options[option]:
0115 options[option] = "'%s'" % options[option]
0116
0117 if options.has_key('containment'):
0118 options['containment'] = array_or_string_for_javascript(options['containment'])
0119 if options.has_key('only'):
0120 options['only'] = array_or_string_for_javascript(options['only'])
0121
0122 return "Sortable.create(%s, %s)" % (json.dumps(element_id), options_for_javascript(options))
0123
0124def draggable_element(element_id, **options):
0125 """
0126 Makes the element with the DOM ID specified by ``element_id`` draggable.
0127
0128 Example::
0129
0130 <% draggable_element("my_image", revert=True)
0131
0132 You can change the behaviour with various options, see
0133 http://script.aculo.us for more documentation.
0134 """
0135 return javascript_tag(draggable_element_js(element_id, **options))
0136
0137def draggable_element_js(element_id, **options):
0138 return "new Draggable(%s, %s)" % (json.dumps(element_id), options_for_javascript(options))
0139
0140def drop_receiving_element(element_id, **options):
0141 """
0142 Makes an element able to recieve dropped draggable elements
0143
0144 Makes the element with the DOM ID specified by ``element_id`` receive
0145 dropped draggable elements (created by draggable_element) and make an
0146 AJAX call By default, the action called gets the DOM ID of the element
0147 as parameter.
0148
0149 Example::
0150
0151 <% drop_receiving_element("my_cart", url=url_for(controller="cart", action="add" )) %>
0152
0153 You can change the behaviour with various options, see
0154 http://script.aculo.us for more documentation.
0155 """
0156 return javascript_tag(drop_receiving_element_js(element_id, **options))
0157
0158def drop_receiving_element_js(element_id, **options):
0159 options.setdefault('with_', "'id=' + encodeURIComponent(element.id)")
0160 options.setdefault('onDrop', "function(element){%s}" % remote_function(**options))
0161 for k in options.keys():
0162 if k in AJAX_OPTIONS: del options[k]
0163
0164 if options.has_key('accept'):
0165 options['accept'] = array_or_string_for_javascript(options['accept'])
0166 if options.has_key('hoverclass'):
0167 options['hoverclass'] = "'%s'" % options['hoverclass']
0168
0169 return "Droppables.add(%s, %s)" % (json.dumps(element_id), options_for_javascript(options))
0170
0171__all__ = ['visual_effect', 'parallel_effects', 'sortable_element', 'draggable_element', 'drop_receiving_element']