mack-javascript 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,64 +1,13 @@
1
+ require File.join_from_here('script_generator')
2
+
1
3
  module Mack
2
4
  module JavaScript # :nodoc:
3
5
  module Framework # :nodoc:
4
- class Jquery
6
+ class JqueryAjax
5
7
  class << self
6
- def insert_html(position, id, html)
7
- insertion = position.to_s.downcase
8
- insertion = 'append' if insertion == 'bottom'
9
- insertion = 'prepend' if insertion == 'top'
10
- "$(\'##{id}\').#{insertion}('#{html}')"
11
- end
12
-
13
- def replace_html(id, html)
14
- insert_html(:html, id, html)
15
- end
16
-
17
- def replace(id, html)
18
- "$(\'##{id}\').replaceWith('#{html}')"
19
- end
20
-
21
- def remove(*ids)
22
- "$(\'##{ids.join(',#')}\').remove()"
23
- end
24
-
25
- def show(*ids)
26
- "$(\'##{ids.join(',#')}\').show()"
27
- end
28
-
29
- def hide(*ids)
30
- "$(\'##{ids.join(',#')}\').hide()"
31
- end
32
-
33
- def toggle(*ids)
34
- "$(\'##{ids.join(',#')}\').toggle()"
35
- end
36
-
37
-
38
- # def draggable(id, options = {})
39
- # record @context.send(:draggable_element_js, id, options)
40
- # end
41
- #
42
- # def visual_effect(name, id = nil, options = {})
43
- # record @context.send(:visual_effect, name, id, options)
44
- # end
45
- #
46
- # def drop_receiving(id, options = {})
47
- # record @context.send(:drop_receiving_element_js, id, options)
48
- # end
49
- #
8
+
50
9
  def remote_function(options)
51
10
  javascript_options = options_for_ajax(options)
52
- update = ''
53
- if options[:update] && options[:update].is_a?(Hash)
54
- update = []
55
- update << "success:'#{options[:update][:success]}'" if options[:update][:success]
56
- update << "failure:'#{options[:update][:failure]}'" if options[:update][:failure]
57
- update = '{' + update.join(',') + '}'
58
- elsif options[:update]
59
- update << "'#{options[:update]}'"
60
- end
61
-
62
11
  function = "$.ajax(#{javascript_options})"
63
12
 
64
13
  function = "#{options[:before]}; #{function}" if options[:before]
@@ -72,19 +21,43 @@ module Mack
72
21
  def options_for_ajax(options)
73
22
  js_options = build_callbacks(options)
74
23
  js_options['url'] = "'#{options[:url]}'"
75
- js_options['async'] = options[:type] != :synchronous
76
- js_options['type'] = options[:method] ? "'#{options[:method]}'" : "'post'"
24
+ js_options['async'] = options[:type] != :synchronous
25
+
26
+
77
27
  js_options['dataType'] = options[:datatype] ? "'#{options[:datatype]}'" : (options[:update] ? nil : "'script'")
78
-
28
+
29
+
79
30
  if options[:form]
80
31
  js_options['data'] = "$.param($(this).serializeArray())"
81
- elsif options[:submit]
82
- js_options['data'] = "$(\"##{options[:submit]}\").serializeArray()"
83
32
  elsif options[:with]
84
33
  js_options['data'] = options[:with].gsub('Form.serialize(this.form)','$.param($(this.form).serializeArray())')
85
34
  end
35
+
36
+ if options[:method].nil? || options[:method].to_sym != :get
37
+ js_options['type'] = "'post'"
38
+ else
39
+ js_options['type'] = "'get'"
40
+ end
41
+
42
+ if options[:method] && options[:method].to_sym == :put || options[:method] == :delete
43
+ js_options['data'] = append_ajax_data(js_options['data'], "_method=#{options[:method]}")
44
+ end
45
+
46
+ if js_options['type'] == "'post'" && options[:authenticity_token]
47
+ js_options['data'] = append_ajax_data(js_options['data'], "__authenticity_token=#{options.delete(:authenticity_token)}")
48
+ end
49
+
86
50
  options_for_javascript(js_options.reject {|key, value| value.nil?})
87
51
  end
52
+
53
+ def append_ajax_data(data, new_data)
54
+ if data
55
+ data << " + '&"
56
+ else
57
+ data = "'"
58
+ end
59
+ data << "#{new_data}'"
60
+ end
88
61
 
89
62
  def options_for_javascript(options)
90
63
  '{' + options.map {|k, v| "#{k}:#{v}"}.sort.join(', ') + '}'
@@ -120,6 +93,509 @@ module Mack
120
93
  end
121
94
 
122
95
  end
96
+
97
+ class JquerySelector < Mack::JavaScript::Selector
98
+
99
+ def select
100
+ "$(#{@selector})"
101
+ end
102
+
103
+
104
+
105
+
106
+ #--- Tree Walking ---#
107
+
108
+ # Will give you the immediate children underneath the selected elements
109
+ #
110
+ #
111
+ # Example:
112
+ # say you have the following html:
113
+ #
114
+ # <div class='rakim'>
115
+ # <ul>
116
+ # ...
117
+ # </ul>
118
+ # </div>
119
+ #
120
+ # <div class='rakim'>
121
+ # <p>Eric B</p>
122
+ # <div id='technique'>
123
+ # ...
124
+ # </div>
125
+ # </div>
126
+ #
127
+ # page.select('.rakim').children would give you a collection consisting of
128
+ # the ul element, the p element, and the div with id 'technique'
129
+ def children
130
+ add "children()"
131
+ end
132
+
133
+ # returns a collection of the immediate parent of each selected element
134
+ def parent
135
+ add "parent()"
136
+ end
137
+
138
+ # returns a collection of every parent up the chain to the root of the document
139
+ # for each selected element. This method also takes an optional selector as an argument.
140
+ #
141
+ # Example:
142
+ #
143
+ # for the html
144
+ #
145
+ # <div class='big_daddy_kane'>
146
+ # <div id='long_live'>
147
+ # <ul class='raw'>
148
+ # <li id='featuring'>with Kool G Rap</li>
149
+ # </ul>
150
+ # </div>
151
+ # </div>
152
+ #
153
+ # page.select('#featuring').ancestors will give you a collection consisting
154
+ # of both divs and the ul.
155
+ # page.select('#featuring').ancestors('#long_live') will give you the div with
156
+ # id 'long_live'
157
+ def ancestors(selector = nil)
158
+ add "parents(#{optional_selector(selector)})"
159
+ end
160
+
161
+ # gets all siblings for each element selected
162
+ # Takes an optional selector as an argument
163
+ def siblings(selector = nil)
164
+ add "siblings(#{optional_selector(selector)})"
165
+ end
166
+
167
+ # gets the next immediate sibling for each element selected
168
+ # Takes an optional selector as an argument
169
+ def next(selector = nil)
170
+ add "next(#{optional_selector(selector)})"
171
+ end
172
+
173
+ # gets the previous immediate sibling for each element selected
174
+ # Takes an optional selector as an argument
175
+ def previous(selector = nil)
176
+ add "prev(#{optional_selector(selector)})"
177
+ end
178
+
179
+ # gets every next sibling for each element selected
180
+ # Takes an optional selector as an argument
181
+ def all_next(selector = nil)
182
+ add "nextAll(#{optional_selector(selector)})"
183
+ end
184
+
185
+ # gets every previous sibling for each element selected
186
+ # Takes an optional selector as an argument
187
+ def all_previous(selector = nil)
188
+ add "prevAll(#{optional_selector(selector)})"
189
+ end
190
+
191
+
192
+
193
+
194
+
195
+ #-- Attributes --#
196
+
197
+ def add_class(klass)
198
+ add "addClass('#{klass}')"
199
+ end
200
+
201
+
202
+ def remove_class(klass = '')
203
+ add "removeClass('#{klass}')"
204
+ end
205
+
206
+ def set_attribute(name, value)
207
+ value = "'#{value}'" if value.is_a? String
208
+ add "attr('#{name}', #{value})"
209
+ end
210
+
211
+ def remove_attribute(name)
212
+ add "removeAttr('#{name}')"
213
+ end
214
+
215
+
216
+
217
+
218
+ #-- DOM Manipulation --#
219
+
220
+ # inserts html into the selected place for the specfied elemets
221
+ #
222
+ # +position+ may be one of:
223
+ #
224
+ # <tt>:top</tt>:: HTML is inserted inside the element, before the
225
+ # element's existing content.
226
+ # <tt>:bottom</tt>:: HTML is inserted inside the element, after the
227
+ # element's existing content.
228
+ # <tt>:before</tt>:: HTML is inserted immediately preceding the element.
229
+ # <tt>:after</tt>:: HTML is inserted immediately following the element.
230
+ #
231
+ #
232
+ # Example
233
+ #
234
+ # <div class='rakim'>
235
+ # <ul>
236
+ # ...
237
+ # </ul>
238
+ # </div>
239
+ #
240
+ # <div class='rakim'>
241
+ # <p>Eric B</p>
242
+ # <div id='technique'>
243
+ # ...
244
+ # </div>
245
+ # </div>
246
+ #
247
+ # page.select('.rakim').insert(:before, "<h1> The R </h1>") would result in:
248
+ #
249
+ # <h1> The R </h2>
250
+ # <div class='rakim'>
251
+ # <ul>
252
+ # ...
253
+ # </ul>
254
+ # </div>
255
+ #
256
+ # <h1> The R </h2>
257
+ # <div class='rakim'>
258
+ # <p>Eric B</p>
259
+ # <div id='technique'>
260
+ # ...
261
+ # </div>
262
+ # </div>
263
+ #
264
+ #
265
+ # Tip: use this with a partial containing your html:
266
+ # page.select('.rakim').insert(:before, render(:partial, 'the_r', :format => :html))
267
+ def insert(position, html)
268
+ position = {:bottom => 'append', :top => 'prepend'}[position.to_sym] || position.to_s
269
+ add "#{position}('#{escape_javascript(html)}')"
270
+ end
271
+
272
+
273
+ # replaces the selected html.
274
+ #
275
+ # +repace+ may be:
276
+ #
277
+ # <tt>:inner</tt>:: The inner html of the selected elements
278
+ # are replaced
279
+ # <tt>:outer</tt>:: the selected elements themselves are replaced
280
+ #
281
+ # Example
282
+ #
283
+ # <div class='rakim'>
284
+ # <p>Dont Sweat the Techinique</p>
285
+ # </div>
286
+ # <div class='rakim'>
287
+ # <p>Follow the Leader</p>
288
+ # </div>
289
+ #
290
+ # page.select('.rakim').replace(:inner, "<p>Paid in Full</p>") would result in
291
+ #
292
+ # <div class='rakim'>
293
+ # <p>Paid in Full</p>
294
+ # </div>
295
+ # <div class='rakim'>
296
+ # <p>Paid in Full</p>
297
+ # </div>
298
+ #
299
+ # if we then did:
300
+ # page.select('.rakim').replace(:outer, "<div class='schoolyD'><p>SaturdayNight</p></div>")
301
+ # the result would be
302
+ #
303
+ # <div class='schoolyD'>
304
+ # <p>SaturdayNight</p>
305
+ # </div>
306
+ # <div class='schoolyD'>
307
+ # <p>SaturdayNight</p>
308
+ # </div>
309
+ def replace(replace, html)
310
+ function = {:inner =>"html",:outer => 'replaceWith'}[replace.to_sym]
311
+ add "#{function}('#{escape_javascript(html)}')"
312
+ end
313
+
314
+ #removes the selected elements from the DOM
315
+ def remove
316
+ add "remove()"
317
+ end
318
+
319
+
320
+
321
+ #-- Effects --#
322
+ #
323
+ # All five effects methods in JquerySelector (morph, effect, show, hide, toggle)
324
+ # take the same options hash. This can consist of:
325
+ #
326
+ # <tt>:duration</tt>:: The duration of the effect in ms.
327
+ # <tt>:easing</tt>:: see below
328
+ # <tt>:callback</tt>:: see below
329
+ # <tt>:queue</tt>:: If false, the animation isn’t queued and begins running
330
+ # immediately.
331
+ #
332
+ #
333
+ # --Easing--
334
+ # This determines the mathematical function your effect will use while transitioning.
335
+ # For instance, if you do page.select(.rakim).effect(:slideUp, :easing => 'easeOutBounce'),
336
+ # every element with class 'rakim' will slide up and when it reaches the top, they will
337
+ # bounce back down a little then go back up. To visualize this better, see
338
+ # http://www.robertpenner.com/easing/easing_demo.html
339
+ #
340
+ # The full list of jquery easing options:
341
+ # linear, swing, easeInQuad, easeOutQuad, easeInOutQuad, easeInCubic, easeOutCubic,
342
+ # easeInOutCubic, easeInQuart, easeOutQuart, easeInOutQuart, easeInQuint, easeOutQuint,
343
+ # easeInOutQuint, easeInSine, easeOutSine, easeInOutSine, easeInExpo, easeOutExpo,
344
+ # easeInOutExpo, easeInCirc, easeOutCirc, easeInOutCirc, easeInElastic, easeOutElastic,
345
+ # easeInOutElastic, easeInBack, easeOutBack, easeInOutBack, easeInBounce, easeOutBounce,
346
+ # easeInOutBounce:
347
+ #
348
+ # --Callback--
349
+ #
350
+ # this is a function that will get called when your effect is done. You can use
351
+ # page.function to build it. Note: This function gets called for each element selected.
352
+ # For instance, in the following example, if there are 10 elements with the class 'rakim'
353
+ # then the function will be called 10 times. If you want to target each of the 10 elements
354
+ # in the callback function, use page.select('this')
355
+ #
356
+ # func = page.function.body do |p|
357
+ # p.select('this').insert(:top, '<h1>Cool Effects</h1>').effect(:highlight)
358
+ # end
359
+ # page.select('.rakim').effect(:slide_down, :callback => func)
360
+ #
361
+ # The above code will make every element with class 'rakim' slide down. When they are done
362
+ # sliding, the h1 tag will be inserted in the top of each element, and each element will
363
+ # be highlighted. Instead of this, you could do the following
364
+ # page.select('.rakim').effect(:slide_down).insert(:top, '<h1>Cool Effects</h1>').effect(:highlight)
365
+ # But this way, the insertion won't wait for the first animation to be done before occurring.
366
+
367
+
368
+
369
+ # Takes a hash of css properties you want the selected elements to 'morph' into.
370
+ # Say you want all elements with class rakim to transition to only having half
371
+ # the opacity and having a red background, and you want the transition to last 4
372
+ # seconds
373
+ # page.select('.rakim').morph({:opacity => 0.5, :backgroundColor => '#f00'}, :duration => 4000)
374
+ # You can see a list of css properties here http://www.w3schools.com/CSS/css_reference.asp
375
+ # The properties in your hash should be camelcase: :backgroundColor instead of
376
+ # background-color
377
+ def morph(hsh, options = nil)
378
+ options[:complete] = options.delete(:callback) if options && options[:callback]
379
+ args = [options_for_javascript(hsh), options_for_effects(options)]
380
+ add "animate(#{args.compact.join(',')})"
381
+ end
382
+
383
+ #This general mapping taken from the awesome JRails plugin
384
+ @@effects = {
385
+ :appear => {:function => 'fadeIn'},
386
+ :blind_down => {:mode => 'blind', :function => 'show', :options => {:direction => 'vertical'}},
387
+ :blind_up => {:mode => 'blind', :function => 'hide', :options => {:direction => 'vertical'}},
388
+ :blind_right => {:mode => 'blind', :function => 'show', :options => {:direction => 'horizontal'}},
389
+ :blind_left => {:mode => 'blind', :function => 'hide', :options => {:direction => 'horizontal'}},
390
+ :bounce_in => {:mode => 'bounce', :function => 'show', :options => {:direction => 'up'}},
391
+ :bounce_out => {:mode => 'bounce', :function => 'hide', :options => {:direction => 'up'}},
392
+ :drop_in => {:mode => 'drop', :function => 'show', :options => {:direction => 'up'}},
393
+ :drop_out => {:mode => 'drop', :function => 'hide', :options => {:direction => 'down'}},
394
+ :fade => {:function => 'fadeOut'},
395
+ :fold_in => {:mode => 'fold', :function => 'hide'},
396
+ :fold_out => {:mode => 'fold', :function => 'show'},
397
+ :grow => {:mode => 'scale', :function => 'show'},
398
+ :highlight => {:mode => 'highlight', :function => 'show'},
399
+ :puff => {:mode => 'puff', :function => 'hide'},
400
+ :pulsate => {:mode => 'pulsate', :function => 'show'},
401
+ :shake => {:mode => 'shake', :function => 'show'},
402
+ :shrink => {:mode => 'scale', :function => 'hide'},
403
+ :slide_down => {:mode => 'slide', :function => 'show', :options => {:direction => 'up'}},
404
+ :slide_up => {:mode => 'slide', :function => 'hide', :options => {:direction => 'up'}},
405
+ :slide_right => {:mode => 'slide', :function => 'show', :options => {:direction => 'left'}},
406
+ :slide_left => {:mode => 'slide', :function => 'hide', :options => {:direction => 'left'}},
407
+ :squish => {:mode => 'scale', :function => 'hide', :options => {:origin => "['top','left']"}},
408
+ :switch_on => {:mode => 'clip', :function => 'show', :options => {:direction => 'vertical'}},
409
+ :switch_off => {:mode => 'clip', :function => 'hide', :options => {:direction => 'vertical'}},
410
+ :toggle_appear => {:function => 'fadeToggle'},
411
+ :toggle_slide => {:mode => 'slide', :function => 'toggle', :options => {:direction => 'up'}},
412
+ :toggle_blind => {:mode => 'blind', :function => 'toggle', :options => {:direction => 'vertical'}},
413
+ }
414
+
415
+ #custom effects. 'name' corresponds to the keys of the hash above
416
+ def effect(name, options = nil)
417
+ effect = @@effects[name]
418
+ args = [effect[:mode] ? "'#{effect[:mode]}'" : nil,
419
+ options_for_effects((effect[:options] || {}).merge(options || {}))]
420
+ add "#{effect[:function]}(#{args.compact.join(',')})"
421
+ end
422
+
423
+ # show() shows an element, hide() hides it, and toggle() shows a hidden and hides a shown.
424
+ #
425
+ # The first parameter, fx, can be any one of the following: blind, bounce, clip, core,
426
+ # drop, explode, fold, highlight, pulsate, scale, shake, slide.
427
+ # You can see above that the majority of the custom effects are built using either
428
+ # show or hide paired with an fx
429
+ # Note: the arguments for the following three methods are unique to jquery
430
+ def show(fx = nil, options = nil)
431
+ args = [fx ? "'#{fx}'" : nil, options_for_effects(options)]
432
+ add "show(#{args.compact.join(',')})"
433
+ end
434
+
435
+ def hide(fx = nil, options = nil)
436
+ args = [fx ? "'#{fx}'" : nil, options_for_effects(options)]
437
+ add "hide(#{args.compact.join(',')})"
438
+ end
439
+
440
+ def toggle(fx = nil, options = nil)
441
+ args = [fx ? "'#{fx}'" : nil, options_for_effects(options)]
442
+ add "toggle(#{args.compact.join(',')})"
443
+ end
444
+
445
+
446
+
447
+
448
+
449
+ #-- Events --#
450
+
451
+ # adds an event listener to the selected elements. If options[:prevent_default] == true
452
+ # the default behavior of the event isn't taken
453
+ #
454
+ # Example
455
+ #
456
+ # page.select('.rakim a').peep 'click', :prevent_default => true do |p|
457
+ # p.select('#sucka_mcs').effect(:fade)
458
+ # end
459
+ #
460
+ # After running this code, if you click any a tag under any element with the
461
+ # class 'rakim', the element with id "sucka_mcs" will fade. Because of the option
462
+ # :prevent_default => true, the default action when clicking the a tag (the browser
463
+ # goes to its href url) isn't done.
464
+ # This can also be used in conjunction with trigger to make and call custom events.
465
+ def peep(event_name, options = {}, &block)
466
+ add "bind('#{event_name}', #{event_function(options[:prevent_default], &block)})"
467
+ end
468
+
469
+ #takes away any event listeners on the 'event_name' event fot the selected elements
470
+ def stop_peeping(event_name)
471
+ add "unbind('#{event_name}')"
472
+ end
473
+
474
+ # triggers the 'event_name' event on the selected elements.
475
+ def trigger(event_name)
476
+ add "trigger('#{event_name}')"
477
+ end
478
+
479
+
480
+
481
+
482
+
483
+ #-- Drag and Drop --#
484
+
485
+ # Makes the selected elements draggable.
486
+ #
487
+ # options can be a hash or a string. The following strings are supported
488
+ #
489
+ # <tt>disable</tt>:: temporarily disables dragging functionality
490
+ # <tt>enable</tt>:: re-enables dragging functionality
491
+ # <tt>destroy</tt>:: disables dragging functionality
492
+ #
493
+ # The options hash has many...well, options, the full list of which
494
+ # can be found here: http://docs.jquery.com/UI/Draggable/draggable#toptions
495
+ # Some usefule ones include:
496
+ #
497
+ # <tt>:opacity</tt>:: opacity of the element once dragging starts
498
+ # <tt>:scroll</tt>:: if true, container auto-scrolls while dragging.
499
+ # <tt>:zIndex</tt>:: z-index for the helper while being dragged.
500
+ # <tt>:helper</tt>:: Possible values: 'original', 'clone'. The 'clone'
501
+ # option will produce the 'ghosting' effect.
502
+ # <tt>:revert</tt>:: if set to true, the element will return to its
503
+ # start position when dragging stops. Also accepts the
504
+ # strings "valid" and "invalid": If set to invalid, revert
505
+ # will only occur if the draggable has not been dropped on
506
+ # a droppable. For valid, it's the other way around.
507
+ def draggable(options = nil)
508
+ add "draggable(#{drag_and_drop_options(options)})"
509
+ end
510
+
511
+
512
+ # Makes the selected elements droppable.
513
+ #
514
+ # options can be a hash or a string. The following strings are supported
515
+ #
516
+ # <tt>disable</tt>:: temporarily disables dropping functionality
517
+ # <tt>enable</tt>:: re-enables dropping functionality
518
+ # <tt>destroy</tt>:: disables dropping functionality
519
+ #
520
+ # all options are at http://docs.jquery.com/UI/Droppables/droppable
521
+ # Some useful ones include:
522
+ # <tt>:accept</tt>:: a css selector that defines what type of draggable
523
+ # elements it accepts. For instance, if :accept => '.block',
524
+ # only draggable objects with the class 'block' will be selected
525
+ # <tt>:activeClass</tt>:: The class that should be added to the droppable while
526
+ # an acceptable draggable is being dragged.
527
+ # <tt>:hoverClass</tt>:: The class that should be added to the droppable while
528
+ # being hovered by an acceptable draggable.
529
+ # <tt>:tolerance</tt>:: Specifies which mode to use for testing whether a draggable
530
+ # is 'over' a droppable. Possible values: 'fit', 'intersect',
531
+ # 'pointer', 'touch'. Default value: 'intersect'.
532
+ # <tt>:remote</tt>:: takes a hash of ajax options, the same as given to page.ajax
533
+ # if this options is, an ajax call is made using the specified
534
+ # options along. Added to the url is an id parameter which
535
+ # has the id of the element that was dropped
536
+ #
537
+ # if a block is given, the blocks code will be executed when a succesful drop is done.
538
+ #
539
+ # Example
540
+ #
541
+ # options = {:accept => '.trash', :remote => {:url => '/stuff'}}
542
+ # page.select('#bucket').droppable options do |p|
543
+ # p.alert('you dropped it!')
544
+ # end
545
+ #
546
+ # This code will make the element with id 'bucket' droppable. If an element with
547
+ # class 'trash' is dropped onto it, an ajax call to the url '/stuff' will be
548
+ # sent with an id parameter of the id of the dropped element. Then an js alert
549
+ # box with the message 'you dropped it' will appear.
550
+ def droppable(options = nil, &block)
551
+ remote_options = options.delete(:remote)
552
+ if remote_options || block_given?
553
+ func = Mack::JavaScript::Function.new(session_id, 'ev', 'ui')
554
+ if remote_options
555
+ remote_options[:with] ||= "'id=' + $(ui.draggable).attr('id')"
556
+ func << Mack::JavaScript::ScriptGenerator.new(session_id).ajax(remote_options)
557
+ end
558
+ func.body(&block) if block_given?
559
+ options.merge!(:drop => func)
560
+ end
561
+ add "droppable(#{drag_and_drop_options(options)})"
562
+ end
563
+
564
+
565
+ private
566
+
567
+ def build_multiple_selector_string(selector)
568
+ "'#{selector.join(', ')}'"
569
+ end
570
+
571
+ def drag_and_drop_options(options)
572
+ options = options.is_a?(String) ? "'#{options}'" : options_for_effects(options)
573
+ end
574
+
575
+ def options_for_effects(options)
576
+ options && !options.empty? ? options_for_javascript(options) : nil
577
+ end
578
+
579
+ def optional_selector(selector)
580
+ return "'#{selector}'" if selector
581
+ nil
582
+ end
583
+
584
+ def options_for_javascript(options)
585
+ options = options.collect do |key, value|
586
+ value = "'#{value}'" unless !value.is_a?(String) || value =~ /^function/
587
+ "#{key}: #{value}"
588
+ end
589
+ "{#{options.join(',')}}"
590
+ end
591
+
592
+ def event_function(prevent_default = false, &block)
593
+ func = Mack::JavaScript::Function.new(session_id, 'event')
594
+ func << "event.preventDefault()" if prevent_default
595
+ func.body(&block)
596
+ end
597
+
598
+ end
123
599
  end
124
600
  end
125
601
  end