lib/mixpanel/tracker.rb in mixpanel-4.0.9 vs lib/mixpanel/tracker.rb in mixpanel-4.1.0
- old
+ new
@@ -33,11 +33,14 @@
def queue
@persist ? @env['rack.session']['mixpanel_events'] : @env['mixpanel_events']
end
def append(type, *args)
- queue << [type, args.collect {|arg| arg.to_json}]
+ js_args = args.collect do |arg|
+ escape_object_for_js(arg).to_json
+ end
+ queue << [type, js_args]
end
protected
def ip
@@ -82,9 +85,77 @@
w.write JSON.dump(data.merge(:_mixpanel_url => url))
1
rescue Errno::EPIPE => e
Mixpanel::Tracker.dispose_worker w
0
+ end
+ end
+
+ private
+
+ # Recursively escape anything in a primitive, array, or hash, in
+ # preparation for jsonifying it
+ def escape_object_for_js(object, i = 0)
+
+ if object.kind_of? Hash
+ # Recursive case
+ Hash.new.tap do |h|
+ object.each do |k, v|
+ h[escape_object_for_js(k, i + 1)] = escape_object_for_js(v, i + 1)
+ end
+ end
+
+ elsif object.kind_of? Enumerable
+ # Recursive case
+ object.map do |elt|
+ escape_object_for_js(elt, i + 1)
+ end
+
+ elsif object.respond_to? :iso8601
+ # Base case - safe object
+ object.iso8601
+
+ elsif object.kind_of?(Numeric)
+ # Base case - safe object
+ object
+
+ elsif [true, false, nil].member?(object)
+ # Base case - safe object
+ object
+
+ else
+ # Base case - use string sanitizer from ActiveSupport
+ escape_javascript(object.to_s)
+
+ end
+ end
+
+ # All this code borrowed from rails/action_pack - ActionView::Helpers::JavascriptHelper
+
+ JS_ESCAPE_MAP = {
+ '\\' => '\\\\',
+ '</' => '<\/',
+ "\r\n" => '\n',
+ "\n" => '\n',
+ "\r" => '\n',
+ '"' => '\\"',
+ "'" => "\\'"
+ }
+
+ JS_ESCAPE_MAP["\342\200\250".force_encoding(Encoding::UTF_8).encode!] = '
'
+ JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] = '
'
+
+ # Escapes carriage returns and single and double quotes for JavaScript segments.
+ #
+ # Also available through the alias j(). This is particularly helpful in JavaScript
+ # responses, like:
+ #
+ # $('some_element').replaceWith('<%=j render 'some/element_template' %>');
+ def escape_javascript(javascript)
+ if javascript
+ javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] }
+ else
+ ''
end
end
end
end