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,50 +1,16 @@
1
+ require File.join_from_here('script_generator')
2
+
1
3
  module Mack
2
4
  module JavaScript
3
5
  module Framework
4
- class Prototype
6
+ class PrototypeAjax
5
7
  @@callbacks = [:uninitialized, :loading, :loaded, :interactive, :complete, :failure, :success ] +
6
8
  [100,101] + (200..206).to_a + (300..307).to_a + (400..417).to_a + (500..505).to_a
7
9
  class << self
8
- def insert_html(position, id, html)
9
- insertion = position.to_s.camelcase
10
- "new Insertion.#{insertion}('#{id}', '#{html}')"
11
- end
12
-
13
- def replace_html(id, html)
14
- "Element.update('#{id}', '#{html}')"
15
- end
16
-
17
- def replace(id, html)
18
- "Element.replace('#{id}', '#{html}')"
19
- end
20
10
 
21
- def remove(*ids)
22
- "#{ids.to_json}.each(Element.remove)"
23
- end
24
-
25
- def show(*ids)
26
- "#{ids.to_json}.each(Element.show)"
27
- end
28
-
29
- def toggle(*ids)
30
- "#{ids.to_json}.each(Element.toggle)"
31
- end
32
11
 
33
- # def draggable(id, options = {})
34
- # record @context.send(:draggable_element_js, id, options)
35
- # end
36
- #
37
- # def visual_effect(name, id = nil, options = {})
38
- # record @context.send(:visual_effect, name, id, options)
39
- # end
40
- #
41
- # def drop_receiving(id, options = {})
42
- # record @context.send(:drop_receiving_element_js, id, options)
43
- # end
44
- #
45
12
  def remote_function(options)
46
13
  javascript_options = options_for_ajax(options)
47
- "new Ajax.Request('#{options[:url]}', #{javascript_options.to_json})"
48
14
  update = ''
49
15
  if options[:update] && options[:update].is_a?(Hash)
50
16
  update = []
@@ -82,13 +48,36 @@ module Mack
82
48
 
83
49
  if options[:form]
84
50
  js_options['parameters'] = 'Form.serialize(this)'
85
- elsif options[:submit]
86
- js_options['parameters'] = "Form.serialize('#{options[:submit]}')"
87
51
  elsif options[:with]
88
52
  js_options['parameters'] = options[:with]
89
53
  end
54
+
55
+ if options[:method].nil? || options[:method].to_sym != :get
56
+ js_options['method'] = "'post'"
57
+ else
58
+ js_options['method'] = "'get'"
59
+ end
60
+
61
+ if options[:method] && options[:method].to_sym == :put || options[:method] == :delete
62
+ js_options['parameters'] = append_ajax_data(js_options['parameters'], "_method=#{options[:method]}")
63
+ end
64
+
65
+ if js_options['method'] == "'post'" && options[:authenticity_token]
66
+ js_options['parameters'] = append_ajax_data(js_options['parameters'], "__authenticity_token=#{options.delete(:authenticity_token)}")
67
+ end
68
+
69
+
90
70
  options_for_javascript(js_options.reject {|key, value| value.nil?})
91
71
  end
72
+
73
+ def append_ajax_data(data, new_data)
74
+ if data
75
+ data << " + '&"
76
+ else
77
+ data = "'"
78
+ end
79
+ data << "#{new_data}'"
80
+ end
92
81
 
93
82
  def options_for_javascript(options)
94
83
  '{' + options.map {|k, v| "#{k}:#{v}"}.sort.join(', ') + '}'
@@ -107,6 +96,385 @@ module Mack
107
96
 
108
97
  end
109
98
  end
99
+
100
+ class PrototypeSelector < Mack::JavaScript::Selector
101
+
102
+ def select
103
+ "$$(#{@selector})"
104
+ end
105
+
106
+ def invoke(arr, options = {})
107
+ arr.collect! do |arg|
108
+ if !arg.is_a?(String) || arg =~ /^function/ || arg =~ /^null$/ || arg =~ /^\{.*\}$/
109
+ arg
110
+ else
111
+ "'#{arg}'"
112
+ end
113
+ end
114
+ function = "invoke(#{arr.compact.join(',')})"
115
+ function << '.flatten().uniq()' if options.delete(:flatten_uniq)
116
+ add function, options
117
+ end
118
+
119
+ def each(func, options = {})
120
+ add "each(function(elem){#{func}})", options
121
+ end
122
+
123
+
124
+ #--- Tree Walking ---#
125
+
126
+ # Will give you the immediate children underneath the selected elements
127
+ #
128
+ #
129
+ # Example:
130
+ # say you have the following html:
131
+ #
132
+ # <div class='rakim'>
133
+ # <ul>
134
+ # ...
135
+ # </ul>
136
+ # </div>
137
+ #
138
+ # <div class='rakim'>
139
+ # <p>Eric B</p>
140
+ # <div id='technique'>
141
+ # ...
142
+ # </div>
143
+ # </div>
144
+ #
145
+ # page.select('.rakim').children would give you a collection consisting of
146
+ # the ul element, the p element, and the div with id 'technique'
147
+ def children
148
+ invoke ["childElements"], :flatten_uniq => true
149
+ end
150
+
151
+ # returns a collection of the immediate parent of each selected element
152
+ def parent
153
+ invoke ["up"], :flatten_uniq => true
154
+ end
155
+
156
+ # returns a collection of every parent up the chain to the root of the document
157
+ # for each selected element.
158
+ def ancestors
159
+ invoke ["ancestors"], :flatten_uniq => true
160
+ end
161
+
162
+ # gets all siblings for each element selected
163
+ def siblings(selector = nil)
164
+ invoke ["siblings", selector], :flatten_uniq => true
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
+ invoke ["next", selector], :flatten_uniq => true
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
+ invoke ["prev", selector], :flatten_uniq => true
177
+ end
178
+
179
+ # gets every next sibling for each element selected
180
+ def all_next
181
+ invoke ["nextSiblings"], :flatten_uniq => true
182
+ end
183
+
184
+ # gets every previous sibling for each element selected
185
+ def all_previous
186
+ invoke ["previousSiblings"], :flatten_uniq => true
187
+ end
188
+
189
+
190
+ #-- Attributes --#
191
+
192
+ def add_class(klass)
193
+ invoke ["addClassName", klass]
194
+ end
195
+
196
+ def remove_class(klass = '')
197
+ invoke ["removeClassName", klass]
198
+ end
199
+
200
+ def set_attribute(name, value)
201
+ value = "null" if value.nil?
202
+ invoke ["writeAttribute", name, value]
203
+ end
204
+
205
+ def remove_attribute(name)
206
+ set_attribute(name, nil)
207
+ end
208
+
209
+
210
+
211
+ #-- DOM Manipulation --#
212
+
213
+ # inserts html into the selected place for the specfied elemets
214
+ #
215
+ # +position+ may be one of:
216
+ #
217
+ # <tt>:top</tt>:: HTML is inserted inside the element, before the
218
+ # element's existing content.
219
+ # <tt>:bottom</tt>:: HTML is inserted inside the element, after the
220
+ # element's existing content.
221
+ # <tt>:before</tt>:: HTML is inserted immediately preceding the element.
222
+ # <tt>:after</tt>:: HTML is inserted immediately following the element.
223
+ #
224
+ #
225
+ # Example
226
+ #
227
+ # <div class='rakim'>
228
+ # <ul>
229
+ # ...
230
+ # </ul>
231
+ # </div>
232
+ #
233
+ # <div class='rakim'>
234
+ # <p>Eric B</p>
235
+ # <div id='technique'>
236
+ # ...
237
+ # </div>
238
+ # </div>
239
+ #
240
+ # page.select('.rakim').insert(:before, "<h1> The R </h1>") would result in:
241
+ #
242
+ # <h1> The R </h2>
243
+ # <div class='rakim'>
244
+ # <ul>
245
+ # ...
246
+ # </ul>
247
+ # </div>
248
+ #
249
+ # <h1> The R </h2>
250
+ # <div class='rakim'>
251
+ # <p>Eric B</p>
252
+ # <div id='technique'>
253
+ # ...
254
+ # </div>
255
+ # </div>
256
+ #
257
+ #
258
+ # Tip: use this with a partial containing your html:
259
+ # page.select('.rakim').insert(:before, render(:partial, 'the_r', :format => :html))
260
+ def insert(position, html)
261
+ invoke ["insert", "{#{position.to_s}: '#{escape_javascript(html)}'}"]
262
+ end
263
+
264
+
265
+ # replaces the selected html.
266
+ #
267
+ # +repace+ may be:
268
+ #
269
+ # <tt>:inner</tt>:: The inner html of the selected elements
270
+ # are replaced
271
+ # <tt>:outer</tt>:: the selected elements themselves are replaced
272
+ #
273
+ # Example
274
+ #
275
+ # <div class='rakim'>
276
+ # <p>Dont Sweat the Techinique</p>
277
+ # </div>
278
+ # <div class='rakim'>
279
+ # <p>Follow the Leader</p>
280
+ # </div>
281
+ #
282
+ # page.select('.rakim').replace(:inner, "<p>Paid in Full</p>") would result in
283
+ #
284
+ # <div class='rakim'>
285
+ # <p>Paid in Full</p>
286
+ # </div>
287
+ # <div class='rakim'>
288
+ # <p>Paid in Full</p>
289
+ # </div>
290
+ #
291
+ # if we then did:
292
+ # page.select('.rakim').replace(:outer, "<div class='schoolyD'><p>SaturdayNight</p></div>")
293
+ # the result would be
294
+ #
295
+ # <div class='schoolyD'>
296
+ # <p>SaturdayNight</p>
297
+ # </div>
298
+ # <div class='schoolyD'>
299
+ # <p>SaturdayNight</p>
300
+ # </div>
301
+ def replace(replace, html)
302
+ function = {:inner =>"update",:outer => 'replace'}[replace.to_sym]
303
+ invoke [function, escape_javascript(html)]
304
+ end
305
+
306
+ #removes the selected elements from the DOM
307
+ def remove
308
+ invoke ['remove']
309
+ end
310
+
311
+
312
+ #-- Effects --#
313
+ #
314
+ # The methods morph and effect take the same options hash. This can consist of:
315
+ #
316
+ # <tt>:duration</tt>:: The duration of the effect in ms.
317
+ # <tt>:easing</tt>:: see below
318
+ # <tt>:fps</tt>:: Specifies the frames-per-second value. The default is 25.
319
+ # <tt>:sync</tt>:: Synchronizes effects when applied in parallel.
320
+ # <tt>:queue</tt>:: Sets the queuing position for effect queues.
321
+ #
322
+ #
323
+ # --Easing--
324
+ # This determines the mathematical function your effect will use while transitioning.
325
+ # For instance, if you do page.select(.rakim).effect(:slideUp, :easing => 'spring'),
326
+ # every element with class 'rakim' will slide up and when it reaches the top, they will
327
+ # bounce back down a little then go back up.
328
+ #
329
+ # The full list of prototype easing options:
330
+ # linear, sinoidal, reverse, flicker, wobble, pulse, spring, none, full
331
+
332
+ def morph(hsh, options = nil)
333
+ #hsh is an object of style values
334
+ invoke ['morph', options_for_javascript(hsh), options_for_effects(options)]
335
+ end
336
+
337
+ # Custom visual effects. Supports the following effect names:
338
+ # highlight, scale, opacity, move, fade, appear, blindUp, blindDown, slideUp, slideDown,
339
+ # dropOut, grow, shrink, puff, switchOff, squish, fold, pulsate, shake, scrollTo
340
+ def effect(name, options = nil)
341
+ invoke ['visualEffect', name.to_s, options_for_effects(options)]
342
+ end
343
+
344
+ # show() shows an element, hide() hides it, and toggle() shows a hidden and hides a shown.
345
+ def show()
346
+ invoke ['show']
347
+ end
348
+
349
+ def hide()
350
+ invoke ['hide']
351
+ end
352
+
353
+ def toggle()
354
+ invoke ['toggle']
355
+ end
356
+
357
+
358
+
359
+ #-- Events --#
360
+
361
+ # adds an event listener to the selected elements. If options[:prevent_default] == true
362
+ # the default behavior of the event isn't taken
363
+ #
364
+ # Example
365
+ #
366
+ # page.select('.rakim a').peep 'click', :prevent_default => true do |p|
367
+ # p.select('#sucka_mcs').effect(:fade)
368
+ # end
369
+ #
370
+ # After running this code, if you click any a tag under any element with the
371
+ # class 'rakim', the element with id "sucka_mcs" will fade. Because of the option
372
+ # :prevent_default => true, the default action when clicking the a tag (the browser
373
+ # goes to its href url) isn't done.
374
+ # This can also be used in conjunction with trigger to make and call custom events.
375
+ def peep(event_name, options = {}, &block)
376
+ invoke ["observe", event_name, event_function(options[:prevent_default], &block)]
377
+ end
378
+
379
+ #takes away any event listeners on the 'event_name' event fot the selected elements
380
+ def stop_peeping(event_name)
381
+ invoke ["stopObserving", event_name]
382
+ end
383
+
384
+ # triggers the 'event_name' event on the selected elements.
385
+ def trigger(event_name)
386
+ invoke ["fire", event_name]
387
+ end
388
+
389
+
390
+
391
+
392
+ #-- Drag and Drop--#
393
+
394
+ # Makes the selected elements draggable.
395
+ #
396
+ # options
397
+ # http://github.com/madrobby/scriptaculous/wikis/draggable
398
+ # <tt>:revert</tt>:: if true, will revert after being dropped. ‘failure’
399
+ # will instruct the draggable not to revert if
400
+ # successfully dropped in a droppable.
401
+ # <tt>:ghosting</tt>:: if true, will clone the element and drag the clone
402
+ # <tt>:zindex</tt>:: The css z-index of the draggable item.
403
+ def draggable(options = nil)
404
+ each "new Draggable(elem, #{options_for_effects(options)})"
405
+ end
406
+
407
+ # Makes the selected elements droppable.
408
+ #
409
+ # options are
410
+ # http://github.com/madrobby/scriptaculous/wikis/droppables
411
+
412
+ # <tt>:accept</tt>:: if set to a css class, the selected elements will only
413
+ # accept elements dragged to it with that class.
414
+ # <tt>:hoverclass</tt>:: a droppable element will have this class added to it
415
+ # when an element is dragged over it.
416
+ # <tt>:remote</tt>:: takes a hash of ajax options, the same as given to page.ajax
417
+ # if this options is, an ajax call is made using the specified
418
+ # options along. Added to the url is an id parameter which
419
+ # has the id of the element that was dropped
420
+ #
421
+ # if a block is given, the blocks code will be executed when a succesful drop is done.
422
+ #
423
+ # Example
424
+ #
425
+ # options = {:remote => {:url => '/stuff'}}
426
+ # page.select('#bucket').droppable options do |p|
427
+ # p.alert('you dropped it!')
428
+ # end
429
+ #
430
+ # This code will make the element with id 'bucket' droppable. If an element is
431
+ # dropped onto it, an ajax call to the url '/stuff' will be sent with an id
432
+ # parameter of the id of the dropped element. Then an js alert
433
+ # box with the message 'you dropped it' will appear.
434
+ def droppable(options = nil, &block)
435
+ remote_options = options.delete(:remote)
436
+ if remote_options || block_given?
437
+ func = Mack::JavaScript::Function.new(session_id, 'elem')
438
+ if remote_options
439
+ remote_options[:with] ||= "'id=' + elem.id"
440
+ func << Mack::JavaScript::ScriptGenerator.new(session_id).ajax(remote_options)
441
+ end
442
+ func.body(&block) if block_given?
443
+ options.merge!(:onDrop => func)
444
+ end
445
+ each "Droppables.add(elem, #{options_for_effects(options)})"
446
+ end
447
+
448
+
449
+ private
450
+
451
+ def build_multiple_selector_string(selector)
452
+ selector.collect{|s| "'#{s}'"}.join(',')
453
+ end
454
+
455
+ def options_for_effects(options)
456
+ return nil unless options
457
+ options[:duration] = (options[:duration]/1000.0) if options[:duration]
458
+ easing = options.delete(:easing)
459
+ options[:transition] = "function(){return Effect.Transitions.#{easing}}()" if easing
460
+ options_for_javascript(options)
461
+ end
462
+
463
+ def options_for_javascript(options)
464
+ options = options.collect do |key, value|
465
+ value = "'#{value}'" unless !value.is_a?(String) || value =~ /^function/
466
+ "#{key}: #{value}"
467
+ end
468
+ "{#{options.join(',')}}"
469
+ end
470
+
471
+ def event_function(prevent_default = false, &block)
472
+ func = Mack::JavaScript::Function.new(session_id, 'event')
473
+ func << "event.stop()" if prevent_default
474
+ func.body(&block)
475
+ end
476
+
477
+ end
110
478
  end
111
479
  end
112
480
  end
@@ -1,20 +1,52 @@
1
+ require File.join_from_here('..', 'view_helpers', 'string_helpers')
2
+
1
3
  module Mack
2
4
  module JavaScript
3
- class ScriptGenerator
4
- def initialize
5
- @lines = ''
5
+ class ScriptGenerator
6
+
7
+ attr_reader :session_id
8
+
9
+ def initialize(session_id = nil)
10
+ @lines = []
11
+ @session_id = session_id
12
+ end
13
+
14
+ # selects elements on the page using css3 selectors
15
+ # For more info: http://www.w3.org/TR/css3-selectors/
16
+ # A few useful examples: 'div' would select all divs. '.full' would
17
+ # select all elements with class 'full'. 'div.blah' would select
18
+ # all divs with class 'blah'. '#foo' would select the element
19
+ # with id 'foo'
20
+ #
21
+ # select can take multiple selector strings. For instance
22
+ # page.select('ul.blah', '#foo', '.full') would give you access to
23
+ # a collection of elements containing all uls with class 'blah', the
24
+ # element with id 'foo' and every element with class 'full'. See
25
+ # JquerySelector or PrototypeSelector for available methods on
26
+ # the returned collection.
27
+ def select(*selector)
28
+ self.class.selector_framework.new(self, *selector)
6
29
  end
7
30
 
8
- def method_missing(sym, *args)
9
- self << self.class.framework.send(sym, *args)
31
+ def ajax(options)
32
+ unless configatron.mack.disable_forgery_detector || !session_id
33
+ options.merge!(:authenticity_token => Mack::Utils::AuthenticityTokenDispenser.instance.dispense_token(session_id))
34
+ end
35
+ self << self.class.ajax_framework.remote_function(options)
10
36
  end
11
37
 
12
- def <<(s)
13
- @lines << s + ";"
38
+ def <<(s, options = {})
39
+ if options[:add_to_last]
40
+ @lines.last << s
41
+ else
42
+ @lines << s
43
+ end
14
44
  end
15
45
 
16
46
  def to_s
17
- @lines
47
+ string = @lines.join(';')
48
+ string << ';' unless string =~ /;$/
49
+ string
18
50
  end
19
51
 
20
52
  def alert(message)
@@ -27,25 +59,82 @@ module Mack
27
59
  args.each {|arg| a << arg.to_json}
28
60
  self << s + a.join(',') + ')'
29
61
  end
62
+
63
+ def function(*args)
64
+ Mack::JavaScript::Function.new(session_id, *args)
65
+ end
30
66
 
31
67
  def assign(variable, value)
32
68
  self << "#{variable} = #{value.to_json}"
33
69
  end
34
70
 
35
71
  def delay(seconds = 1, &block)
36
- self << "setTimeout(function() {\n\n" + yield(Mack::JavaScript::ScriptGenerator.new) + "}, #{(seconds * 1000).to_i})"
72
+ self << "setTimeout(#{function.body(&block)}, #{(seconds * 1000).to_i})"
73
+ end
74
+
75
+
76
+ #-- Deprecated Methods --#
77
+
78
+ def insert_html(position, id, html)
79
+ deprecate_method(:insert_html, 'page.select("#id").insert(position, html)', '0.8.3')
80
+ self.select("##{id}").insert(position, html)
81
+ end
82
+
83
+ def replace_html(id, html)
84
+ deprecate_method(:replace_html, 'page.select("#id").replace(:inner, html)', '0.8.3')
85
+ self.select("##{id}").replace(:inner, html).to_s
86
+ end
87
+
88
+ def replace(id, html)
89
+ deprecate_method(:replace, 'page.select("#id").replace(:outer, html)', '0.8.3')
90
+ self.select("##{id}").replace(:outer, html).to_s
91
+ end
92
+
93
+ def remove(*ids)
94
+ deprecate_method(:remove, 'page.select("#id1", "#id2").remove', '0.8.3')
95
+ ids = [ids] if ids.is_a? String
96
+ ids.collect! {|id| "##{id}"}
97
+ self.select(*ids).remove
98
+ end
99
+
100
+ def show(*ids)
101
+ deprecate_method(:show, 'page.select("#id1", "#id2").show', '0.8.3')
102
+ ids = [ids] if ids.is_a? String
103
+ ids.collect! {|id| "##{id}"}
104
+ self.select(*ids).show
105
+ end
106
+
107
+ def hide(*ids)
108
+ deprecate_method(:hide, 'page.select("#id1", "#id2").hide', '0.8.3')
109
+ ids = [ids] if ids.is_a? String
110
+ ids.collect! {|id| "##{id}"}
111
+ self.select(*ids).hide
112
+ end
113
+
114
+ def toggle(*ids)
115
+ deprecate_method(:toggle, 'page.select("#id1", "#id2").toggle', '0.8.3')
116
+ ids = [ids] if ids.is_a? String
117
+ ids.collect! {|id| "##{id}"}
118
+ self.select(*ids).toggle
37
119
  end
38
120
 
39
121
  class << self
40
122
 
41
- def framework
42
- ivar_cache('framework_constant') do
43
- "Mack::JavaScript::Framework::#{framework_name}".constantize
123
+ def ajax_framework
124
+ ivar_cache('ajax_framework') do
125
+ "Mack::JavaScript::Framework::#{framework_name}Ajax".constantize
126
+ end
127
+ end
128
+
129
+ def selector_framework
130
+ ivar_cache('selector_framework') do
131
+ "Mack::JavaScript::Framework::#{framework_name}Selector".constantize
44
132
  end
45
133
  end
46
134
 
47
135
  def framework=(args)
48
- @framework_constant = nil
136
+ @ajax_framework = nil
137
+ @selector_framework = nil
49
138
  @@framework_name = args.camelcase
50
139
  end
51
140
 
@@ -54,7 +143,55 @@ module Mack
54
143
  @@framework_name ||= configatron.mack.js_framework.camelcase
55
144
  end
56
145
  end
146
+ end
147
+
148
+ class Selector
149
+ include Mack::ViewHelpers::StringHelpers
150
+
151
+ attr_reader :session_id
152
+
153
+ def initialize(generator, *sel)
154
+ @generator = generator
155
+ @session_id = generator.session_id
156
+ @selector = sel.first == 'this' ? 'this' : build_multiple_selector_string(sel)
157
+ @generator << select
158
+ end
159
+
160
+ def add(statement, options = {})
161
+ @generator.<<(".#{statement}", :add_to_last => true)
162
+ self
163
+ end
57
164
 
58
165
  end
166
+
167
+
168
+ class Function
169
+
170
+ def initialize(session_id = nil, *args)
171
+ if args.first.is_a? Fixnum
172
+ args = Array.new(args.first){|i| i + 1}.collect{|x| "obj#{x}"}
173
+ end
174
+ @session_id = session_id
175
+ @arguments = args
176
+ @generator = Mack::JavaScript::ScriptGenerator.new(session_id)
177
+ end
178
+
179
+ def <<(*args)
180
+ @generator << args
181
+ to_s
182
+ end
183
+
184
+ def body(&block)
185
+ yield @generator
186
+ to_s
187
+ end
188
+
189
+
190
+ def to_s
191
+ "function(#{@arguments.join(', ')}){#{@generator.to_s}}"
192
+ end
193
+
194
+
195
+ end
59
196
  end
60
- end
197
+ end
@@ -8,10 +8,14 @@ module Mack
8
8
  if io.is_a?(File)
9
9
  io = io.read
10
10
  end
11
- @_jsp_page = Mack::JavaScript::ScriptGenerator.new
11
+ @_jsp_page = Mack::JavaScript::ScriptGenerator.new(view_template.controller.session.id)
12
12
  view_template.instance_variable_set("@_jsp_page", @_jsp_page)
13
13
  eval(io, binding)
14
- @_jsp_page.to_s
14
+ resp = @_jsp_page.to_s
15
+ if Mack.env == 'development'
16
+ resp = "try {#{resp}}catch(e){alert('RJS error:\\n\\n' + e.toString());throw e};"
17
+ end
18
+ resp
15
19
  end
16
20
 
17
21
  def extension