Fork me on GitHub

Drew's World

Rants, News, Etc on my Life and Projects

jQuery Comparative Histogram Plugin

by Andrew De Ponte (@cyphactor)

I recently wrote a jQuery Plugin that I am calling jquery_comphist. It is a jQuery Plugin that draws a Comparative Histogram of a provided population divided into two major groups with any number of shared sub-groups. One of the most common uses for such a feature is when you are trying to display demographics data on some population. Anyways, as usual the code is available at my GitHub page here and you can see a demo of it below.

 

With the jQuery Comparative Histogram (CompHist) Plugin you can easily represent populations and information about them. A prime usage exampe of this is demographics. All the styling is done via CSS. Hence the style is easily modified. It does the calculations for you and it allows for non listed group counts via a non_demo_count data option.

The above demo was produced by defining a few code snippets. The first snippet is a snippet of HTML that includes the jQuery Comparative Histogram javascript source file and CSS style sheet. Note: The jQuery Comparative Histogram javascript source file needs to come after the standard jQuery javascript source file. An example of this snippet is as follows:

<script type="text/javascript" src="../jquery.comphist.js"></script>
<link rel="stylesheet" type="text/css" href="../stylesheets/jquery.comphist.css">

Note: In the above snippet the href values are likely to be different in your case. The second code snippet is simply a single line of HTML where the div that is to be the Comparative Histogram. It is key that this div has an id that is easily referenced and a set height and width. An example of this snippet is as follows:

<div id="compplaceholder" style="width: 375px; height: 200px;"></div>

The third code snippet needed is the javascript that defines the data, creates the Comparative Histogram in the specified div, and handles the hover functionality. An example of this is seen below.

<script type="text/javascript">
$(function () {
  function showCompHistTooltip(x, y, contents) {
    console.log("called my shit");
    $('<div id="comphist1tooltip">' + contents + '</div>').css({
      position: 'absolute',
      display: 'none',
      top: y - 20,
      left: x - 30,
      border: '1px solid #fdd',
      padding: '2px',
      'background-color': '#fee',
      opacity: 1
    }).appendTo("body").fadeIn(200);
  }

  var comphist_data = {
    population_label: 'Active Fans This Week',
    group_1_label: 'Male', group_2_label: 'Female',
    subgroups: [
    { label: '13-17', group_1_count: 3, group_2_count: 16 },
    { label: '18-24', group_1_count: 7, group_2_count: 27 },
    { label: '25-34', group_1_count: 3, group_2_count: 29 },
    { label: '35-44', group_1_count: 2, group_2_count: 9 },
    { label: '45-54', group_1_count: 0, group_2_count: 4 },
    { label: '55+', group_1_count: 0, group_2_count: 0 }
    ],
    non_demo_count: 0
  };
  $("#compplaceholder").comphist({}, comphist_data);

  $("#compplaceholder").bind("barover", function (e, perc, pos) {
    $("#comphist1tooltip").remove();
    showCompHistTooltip(pos.left, pos.top, perc);
  });

  $("#compplaceholder").bind("barout", function (e, perc, pos) {
    $("#comphist1tooltip").remove();
  });
});
</script>

Hopefully, the demo and quick code example helps get you up and running quickly.

Flot Multi-series Bar Graph Support

by Andrew De Ponte (@cyphactor)

Hey boys and girls. I just got finished coding support for Multi-series bar graphs into Flot (a jQuery based JavaScript graphing library). Hence, I figured I would share not only the code but a little preview of it below. The code for it can be found at my GitHub account here. Please enjoy the little demo below.

 

Mouse hovers at (0, 0).

With the multi-series bar graph support, you can have Flot display the series side by side. This is useful when you are interested in visually comparing series and grouping segments you want to columns in series that you are specifically interested in comparing.

The above demo was produced by defining a few code snippets. The first snippet is a snippet of HTML that includes the customized Flot javascript source file. Note: The Flot javascript source file needs to come after the standard jQuery javascript source file. An example of this snippet is as follows:

<!--[if IE]><script language="javascript" type="text/javascript" src="../excanvas.min.js"></script><![endif]-->
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>

Note: In the above snippet the href values are likely to be different in your case. The second code snippet is simply a single line of HTML that is the div that is to be the Multi-series Bar Graph. It is key that this div has an id that is easily referenced and a set height and width. An example of this snippet is as follows:

<div id="placeholder" style="width: 375px; height: 200px;"></div>

The third code snippet needed is the javascript that defines the data, creates the Multi-series Bar Graph in the specified div, and handles the hover functionality. An example of this is seen below.

<script id="source">
$(function () {
  var ms_data = [{"label":"FOO","data":[[0,80],[1,70],[2,100],[3,60],[4,102]]},
                 {"label":"BAR","data":[[0,10],[1,20],[2,30],[3,40],[4,80]]},
                 {"label":"CAR","data":[[0,5],[1,10],[2,15],[3,20],[4,25]]}]
  var ms_ticks = [[0,"1/28"],[1,"1/29"],[2,"1/30"],[3,"1/31"],[4,"1/32"]];

    function plotWithOptions() {
      $.plot($("#placeholder"), ms_data, {
        bars: { show: true, barWidth: 0.6, series_spread: true, align: "center" },
        xaxis: { ticks: ms_ticks, autoscaleMargin: .10 },
        grid: { hoverable: true, clickable: true }
      });
    }

    function showTooltip(x, y, contents) {
        $('').css( {
            position: 'absolute',
            display: 'none',
            top: y + 5,
            left: x + 5,
            border: '1px solid #fdd',
            padding: '2px',
            'background-color': '#fee',
            opacity: 0.80
        }).appendTo("body").show();
    }

    plotWithOptions();

    $("#placeholder").bind("plothover", function (event, pos, item) {
      $("#x").text(pos.x.toFixed(2));
      $("#y").text(pos.y.toFixed(2));
        if (item) {
            if (previousPoint != item.datapoint) {
                previousPoint = item.datapoint;

                $("#tooltip").remove();
                var x = item.datapoint[0].toFixed(2),
                    y = item.datapoint[1].toFixed(2);

                showTooltip(item.pageX, item.pageY,
                            item.series.label + " Group id: " + Math.floor(x) + ", y = " + y + ", seriesIndex: " + item.seriesIndex);
            }
        }
        else {
            $("#tooltip").remove();
            previousPoint = null;            
        }
    });

    $("#placeholder").bind("plotclick", function (event, pos, item) {
        if (item) {
            $("#clickdata").text("You clicked bar " + item.dataIndex + " in " + item.series.label + ".");
        }
    });
});
</script>

Hopefully, the demo and quick code example helps get you up and running.

Ajax Request Generated JavaScript

Hi all. Recently I was working on a web application that utilized JavaScript and AJAX. Who would have guessed. Anyways, I ran into a bit of a snag when I had the AJAX requests result defining a JavaScript snippet. The problem was that AJAX requests strip out script tags and don’t let them get inserted into the DOM tree. I am not sure why this is the case, maybe for some security reason that I have yet to see. Anyways, this creates a bit of a problem when you are trying to do things like use a JavaScript calendar widget inside the AJAX result set when the calendar widget needs to be initialized by some JavaScript code that is unique to that calendar.

Despite all my efforts in googling I failed to find a clearly defined solution. Bits of the sites have clues but none of them had actual solutions with enough detail to make it reproducible. Hence, after exploring all of these hints and all of the combinations of the hints I managed to find a working solution for using JavaScript that is dynamically generated as part of the result of an AJAX call.

Lets assume that you are using Prototype because I was, and it is a very common AJAX JavaScript library. The first thing I looked to find out was if there was an event fired off to the portion that is generated by AJAX request. The answer is, not that I could find. There are events in the top level document but none which really help you. Since, there are no events or callbacks in the child document (one produced by the ajax call and loaded into the top level) and no script tags carried through to the top level document we first have to figure out a way to get the JavaScript code to the top level document and then secondarily have JavaScript evaluate it.

In order to get the script tags content to the top level document I had to create a div that contained the raw content of the script tag that I wanted available. Beyond that the div had to have a unique ID that was able to be obtained programatically in the top level document using JavaScript. An example of this might look as follows (html.erb template of JavaScript):

<div id="<%= calendar_id %>_script" style="display: none;">
  <%= calendar_id %>SelectHandler = function(type, args, obj){
    var selected = args[0];
    var selDate = selected[0][0] + "-" + selected[0][1] + "-" + selected[0][2];
    <% if txt_input_id.nil? %>
      document.getElementById("<%= calendar_namespace %>_<%= calendar_id %>").value = selDate;
      <%= calendar_id %>hide();
    <% else %>
      document.getElementById("<%= txt_input_id %>").value = selDate;
      <%= calendar_id %>hide();
    <% end %>
  }

  <%= calendar_id %>show = function (){
    var cur_z = $('<%= calendar_container %>').style.zIndex;
    if (!cur_z) {
      cur_z = 1;
    } else {
      cur_z = parseInt(cur_z);
    }
    $('<%= calendar_container %>').show();
    $('<%= calendar_container %>').style.zIndex = (cur_z + 1);
  }

  <%= calendar_id %>hide = function(){
    var cur_z = $('<%= calendar_container %>').style.zIndex;
    if (!cur_z) {
      cur_z = 1;
    } else {
      cur_z = parseInt(cur_z);
    }
    $('<%= calendar_container %>').hide();
    $('<%= calendar_container %>').style.zIndex = (cur_z - 1);
  }

  YAHOO.namespace("<%= calendar_namespace %>.<%= calendar_id %>");
  YAHOO.<%= calendar_namespace %>.<%= calendar_id %>.init = function() {
    YAHOO.<%= calendar_namespace %>.<%= calendar_id %>.Cal = new YAHOO.widget.Calendar("<%= calendar_id %>","<%= calendar_container %>", <%= @opts %>);

    YAHOO.<%= calendar_namespace %>.<%= calendar_id %>.Cal.selectEvent.subscribe(<%= calendar_id %>SelectHandler, YAHOO.<%= calendar_namespace %>.<%= calendar_id %>.Cal, true);
    YAHOO.<%= calendar_namespace %>.<%= calendar_id %>.Cal.render();
  }
</div>

This div with a unique id gives me the capability of pulling out the associated raw JavaScript that is the innerHTML content of this div in the top level document and having the top level documents JavaScript evaluate it. This can be done with a JavaScript function like the following:

function createRowEditCalendar(id) {
  var script = document.getElementById("cal_visited_on_" + id + "_script").innerHTML;
  eval(script);
  var my_init_cmd = "YAHOO.namespace_o_hell.cal_visited_on_" + id + ".init();";
  eval(my_init_cmd);
  return true;
}

The next logical question of course is what event or callback launches the above JavaScript function in the top level document. The answer in this case is simply to use the Prototypes Ajax.Updater onComplete callback by having it first programatically figure out the name of the div using the reproducible naming scheme and then call the above JavaScript function passing it the necessary id to access the hidden JavaScript div. This might look something like the following:

<input type="button" id="rowedit_create_button" value="Create" onclick="new Ajax.Updater(<% if prefix %> '<%= prefix %>_rowedit_data_rows' <% else %> 'rowedit_data_rows' <% end %>, '<%= urls[:create] %>', {asynchronous:true, parameters:$(<% if prefix %> '<%= prefix %>_rowedit_create_form' <% else %> 'rowedit_create_form' <% end %>).serialize(), insertion: Insertion.Bottom, onComplete: function() { var drows = document.getElementById('rowedit_data_rows'); var last_row_id = drows.lastChild.id; last_row_id = last_row_id.replace('edit_', ''); last_row_id = last_row_id.replace('_row', ''); createRowEditCalendar(last_row_id); } });"/>

Assuming that you have those three components in place and properly setup with all the syntax correct you should have a working solution. Note: Be very careful of syntax errors in the JavaScript inside the eval() method because FireBug won’t detect them, or run-time errors in the JavaScript anywhere because not all of them will be detected by FireBug. Anyways, hope this helps someone out there, it sure would have helped me if I had it.