mack-javascript 0.8.2 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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