Documentation for gRaphael: g.line.js

Update: I found another library that is superior to gRaphael. It’s called Ico by Alex Young. You can find it here: http://alexyoung.github.com/ico/. It doesn’t support all of the graph types, but the options more thorough.

gRaphael provides an easy way of creating charts through JavaScript. Unfortunately, many people have complained about how poorly it is documented. In the interest of seeing more good looking charts on websites, I decided to go through the code and post documentation here. This first post is on the line chart library, but I will be posting more on the rest of the library in the future.

To start using the line chart, we need to include the Raphael and the line chart library:


<script src="/script/raphael.js" type="text/javascript" charset="utf-8"></script>
<script src="/script/g.raphael.js" type="text/javascript" charset="utf-8"></script>
<script src="/script/g.line.js" type="text/javascript" charset="utf-8"></script>

Then initialize the chart by passing in the options to the constructor:


var r = Raphael("element");
r.g.linechart(x_offset, y_offset, width, height, x_values_array, y_values_array, options);

x_values_array is either an array of x values, or an array of multiple arrays of x values.
y_values_array is the same as above, but for the y-axis.
If only one array of x values is specified, multiple y value arrays will share these values. If both are specified, each plot will be completely independent of each other.

options is an object with the following possible elements:

var options = {
  gutter: 10,
  symbol: "",
  colors: Raphael.fn.g.colors,
  nostroke: false,
  smooth: false;
  shade: false,
  dash: "",
  axis: "",
  axisxstep: 1,
  axisystep: 1
};

options.gutter is the default spacing between the edge of the chart area and the graph itself.

options.symbol is either a single symbol or an array of multiple symbols for each line. Just leaving it as a blank (“”) means that it will be a line graph without any decoration at the points. The list of allowable options is:

"" - no symbol
"o" - "disc"
"f" - "flower"
"d" - "diamond"
"s" - "square"
"t" - "triangle"
"*" - "star"
"x" - "cross"
"+" - "plus"
"->"; - "arrow"

options.colors must be an array, even if there is only one line.

options.nostroke controls the line drawing. Set to false (default), it will draw the line. If this is set to true and a symbol is chosen, a scatter type plot can be made.

options.smooth turns on smoothing. No jagged angles will be seen, just smooth curves.

options.shade controls shading the area below the line. No shading happens by default. Set this to true to create an area chart.

options.dash sets the ‘stroke-dasharray’ property on the line element. According to the SVG spec:

‘stroke-dasharray’ controls the pattern of dashes and gaps used to stroke paths. Contains a list of comma and/or white space separated integers that specify the lengths of alternating dashes and gaps. If an odd number of values is provided, then the list of values is repeated to yield an even number of values. Thus, stroke-dasharray: 5,3,2 is equivalent to stroke-dasharray: 5,3,2,5,3,2.

options.axis is a comma or space separated list of axes to show. Seems like it follows the order of css lists: top, right, bottom, left.

option.axisxstep determines how far apart the ticks and axis numbers are placed.

option.axisystep is the same as above, but for the y-axis.

Unfortunately, this is as far as the axis customization goes. I’ve been looking at the parent object and it looks like it wouldn’t be hard to customize the axes further. I’ll post more on this as I figure it out. If anyone wants to look at the g.axis() function and figure out how to add labels and customize the labels on the axis, please let me know what you find.

r.g.linechart() returns a chart object:

chart = {lines, shades, symbols, axis, columns, dots};

That chart object has the following default functions. All of these are overridable.

chart.hoverColumn = function (fin, fout) {
  !columns && createColumns();
  columns.mouseover(fin).mouseout(fout);
  return this;
};

chart.clickColumn = function (f) {
  !columns && createColumns();
  columns.click(f);
  return this;
};

chart.hrefColumn = function (cols) {
  var hrefs = that.raphael.is(arguments[0], "array") ? arguments[0] : arguments;
  if (!(arguments.length - 1) && typeof cols == "object") {
    for (var x in cols) {
      for (var i = 0, ii = columns.length; i < ii; i++) if (columns[i].axis == x) {
        columns[i].attr("href", cols[x]);
      }
    }
  }
  !columns && createColumns();
  for (i = 0, ii = hrefs.length; i < ii; i++) {
    columns[i] && columns[i].attr("href", hrefs[i]);
  }
  return this;
};

chart.hover = function (fin, fout) {
  !dots && createDots();
  dots.mouseover(fin).mouseout(fout);
  return this;
};

chart.click = function (f) {
  !dots && createDots();
  dots.click(f);
  return this;
};

chart.each = function (f) {
  createDots(f);
  return this;
};

chart.eachColumn = function (f) {
  createColumns(f);
  return this;
};
Advertisements

Browser Detection: Why Feature Detection is Better

I wanted to figure out how to make my websites more tailored to the browser they were being viewed in. I figured browser detection might work. So I set up bit of PHP to log user agent strings sent to my server for the http://adaburrows.com/test site. I then tweeted about it and watched the user agent strings roll in.


<?php
/* Code that opens a log file called agent.log */
$log = fopen('agent.log', 'a+');
/* Write agent string, IP address, current PHP file w/ full path, and full date */
fputcsv($log, Array($_SERVER['HTTP_USER_AGENT'], $_SERVER['REMOTE_ADDR'], __FILE__, date('r')));
/* close the file */
fclose($log);
?>

Here are some sample agent strings: (try and guess what browsers they are from!)

“Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 GTB6”
“Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)”
“Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.5) Gecko/20091109 Ubuntu/9.10 (karmic) Firefox/3.5.5”
“AppEngine-Google; (+http://code.google.com/appengine; appid: mapthislink)”
“Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; WOW64; Trident/4.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30618)”
PycURL/7.18.2
“Mozilla/5.0 (compatible; MSIE 6.0b; Windows NT 5.0) Gecko/2009011913 Firefox/3.0.6 TweetmemeBot”
bitlybot
“Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.5 Safari/532.2”

I learned a lot about different bots that crawl the internet, I also learned about how unreliable these strings are. I decided against using browser detection in any of my sites. Some friends on twitter were having a discussion about this and since I was looking into it anyway, I decided to write this and included info from their talk. They had some interesting resources that they brought up. One of which was a history overview of browser identifications. Another resource on the history is here. This gives some insight into why browser detection using the agent string is the most absurd way of grasping for what capabilities are available.

In order to actually use the user agent string, one need to have a database of all browser agent strings and all the capabilities that browser has. There are projects out there, and PHP actually supports doing this with if one has a browsercap.ini file, but this adds latency to one’s web site. According to some studies, any extra latency (even half a second) will reduce returning web traffic. It is also nowhere near being bullet proof. If the capabilities for a certain browser are absent from that file, then those browsers are discriminated against (a hall of shame is available here).

Feature Detection

The best method to actually use is called feature detection. It is client side only and uses Javascript, but it reinforces use of web standards. It lets one test if features are available in a browser and conditionally add extra features to your site based on the actual capabilities of the browser. It completely sidesteps the pitfalls of using the agent string and it is a part of best practices.

In order to use this method, all website must adhere to certain principles. These are all principles that websites should be following:

  • Use semantic HTML markup
  • Separate presentation from markup
  • Provide basic functionality for non-Javascript browsers as a fall back.
  • The site should degrade gracefully and still be usable (even in lynx, I use that text only browser sometimes!).

I’ll come back to writing more resources on this soon, but here’s the best article on that I’ve seen. This page at Mozilla has some serious code for feature detection.

A great project that showcases great use of feature detection is Modenizr (Paul Irish is their code lieutenant). Check it out and help make a typography renaissance on the web!