<commit_before>require 'rack'

class MixpanelMiddleware
  def initialize(app, mixpanel_token)
    @app = app
    @token = mixpanel_token
  end

  def call(env)
    @env = env

    @status, @headers, @response = @app.call(env)
    
    update_response!
    update_content_length!
    delete_event_queue!
    
    [@status, @headers, @response]
  end

  private

  def update_response!
    @response.each do |part|
      if is_regular_request? && is_html_response?
        insert_at = part.index('</head')
        unless insert_at.nil?
          part.insert(insert_at, render_mixpanel_scripts)
          part.insert(insert_at, render_event_tracking_scripts) unless queue.empty?
        end
      elsif is_ajax_request? && is_html_response?
        part.insert(0, render_event_tracking_scripts) unless queue.empty?
      elsif is_ajax_request? && is_javascript_response?
        part.insert(0, render_event_tracking_scripts(false)) unless queue.empty?
      end
    end
  end

  def update_content_length!
    new_size = 0
    @response.each{|part| new_size += part.bytesize}
    @headers.merge!("Content-Length" => new_size.to_s)
  end

  def is_regular_request?
    !is_ajax_request?
  end

  def is_ajax_request?
    @env.has_key?("HTTP_X_REQUESTED_WITH") && @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
  end

  def is_html_response?
    @headers["Content-Type"].include?("text/html") if @headers.has_key?("Content-Type")
  end

  def is_javascript_response?
    @headers["Content-Type"].include?("text/javascript") if @headers.has_key?("Content-Type")
  end

  def render_mixpanel_scripts
    <<-EOT
  <script type='text/javascript'>
    var mp_protocol = (('https:' == document.location.protocol) ? 'https://' : 'http://');
    document.write(unescape('%3Cscript src="' + mp_protocol + 'api.mixpanel.com/site_media/js/api/mixpanel.js" type="text/javascript"%3E%3C/script%3E'));
  </script>
  <script type='text/javascript'>
    try {
      var mpmetrics = new MixpanelLib('#{@token}');
    } catch(err) {
      null_fn = function () {};
      var mpmetrics = {
        track: null_fn,  track_funnel: null_fn,  register: null_fn,  register_once: null_fn, register_funnel: null_fn
      };
    }
  </script>
    EOT
  end

  def delete_event_queue!
    @env.delete('mixpanel_events')
  end

  def queue
    return [] if !@env.has_key?('mixpanel_events') || @env['mixpanel_events'].empty?
    @env['mixpanel_events']
  end

  def render_event_tracking_scripts(include_script_tag=true)
    return "" if queue.empty?

    output = queue.map {|event| %(mpmetrics.track("#{event[:event]}", #{event[:properties].to_json});) }.join("\n")

    output = "try {#{output}} catch(err) {}"

    include_script_tag ? "<script type='text/javascript'>#{output}</script>" : output
  end
end<commit_msg>Added support for mixpanel's asynchronous javascript mechanism.<commit_after>require 'rack'

class MixpanelMiddleware
  def initialize(app, mixpanel_token, options={})
    @app = app
    @token = mixpanel_token
    @options = {
      :async => false
    }.merge(options)
  end

  def call(env)
    @env = env

    @status, @headers, @response = @app.call(env)
    
    update_response!
    update_content_length!
    delete_event_queue!
    
    [@status, @headers, @response]
  end

  private

  def update_response!
    @response.each do |part|
      if is_regular_request? && is_html_response?
        insert_at = part.index('</head')
        unless insert_at.nil?
          part.insert(insert_at, render_mixpanel_scripts)
          part.insert(insert_at, render_event_tracking_scripts) unless queue.empty?
        end
      elsif is_ajax_request? && is_html_response?
        part.insert(0, render_event_tracking_scripts) unless queue.empty?
      elsif is_ajax_request? && is_javascript_response?
        part.insert(0, render_event_tracking_scripts(false)) unless queue.empty?
      end
    end
  end

  def update_content_length!
    new_size = 0
    @response.each{|part| new_size += part.bytesize}
    @headers.merge!("Content-Length" => new_size.to_s)
  end

  def is_regular_request?
    !is_ajax_request?
  end

  def is_ajax_request?
    @env.has_key?("HTTP_X_REQUESTED_WITH") && @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
  end

  def is_html_response?
    @headers["Content-Type"].include?("text/html") if @headers.has_key?("Content-Type")
  end

  def is_javascript_response?
    @headers["Content-Type"].include?("text/javascript") if @headers.has_key?("Content-Type")
  end

  def render_mixpanel_scripts
    if @options[:async]
        <<-EOT
      <script type='text/javascript'>
        var mpq = [];
        mpq.push(["init", "#{@token}"]);
        (function() {
        var mp = document.createElement("script"); mp.type = "text/javascript"; mp.async = true;
        mp.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + "//api.mixpanel.com/site_media/js/api/mixpanel.js";
        var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(mp, s);
        })();
      </script>
        EOT
    else
      <<-EOT
    <script type='text/javascript'>
      var mp_protocol = (('https:' == document.location.protocol) ? 'https://' : 'http://');
      document.write(unescape('%3Cscript src="' + mp_protocol + 'api.mixpanel.com/site_media/js/api/mixpanel.js" type="text/javascript"%3E%3C/script%3E'));
    </script>
    <script type='text/javascript'>
      try {
        var mpmetrics = new MixpanelLib('#{@token}');
      } catch(err) {
        null_fn = function () {};
        var mpmetrics = {
          track: null_fn,  track_funnel: null_fn,  register: null_fn,  register_once: null_fn, register_funnel: null_fn
        };
      }
    </script>
      EOT
    end
  end

  def delete_event_queue!
    @env.delete('mixpanel_events')
  end

  def queue
    return [] if !@env.has_key?('mixpanel_events') || @env['mixpanel_events'].empty?
    @env['mixpanel_events']
  end

  def render_event_tracking_scripts(include_script_tag=true)
    return "" if queue.empty?

    if @options[:async]
      output = queue.map {|event| %(mpq.push(["track", "#{event[:event]}", #{event[:properties].to_json}]);) }.join("\n")
    else
      output = queue.map {|event| %(mpmetrics.track("#{event[:event]}", #{event[:properties].to_json});) }.join("\n")
    end

    output = "try {#{output}} catch(err) {}"

    include_script_tag ? "<script type='text/javascript'>#{output}</script>" : output
  end
end
{'fraction_non_alphanumeric': 0.1462922742786224, 'fraction_numerical': 0.0023270245113248527, 'mean_word_length': 3.4278846153846154, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 20, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5090165', 'n_tokens_mistral': 2182, 'n_tokens_neox': 2143, 'n_words': 395}