Index: www/controllers/plot.php
===================================================================
--- www/controllers/plot.php	(revision 1025)
+++ www/controllers/plot.php	(working copy)
@@ -3,8 +3,8 @@
 require_once(IA_ROOT_DIR."common/db/user.php");
 require_once(IA_ROOT_DIR."common/db/score.php");
 
-// This controller serves real time plots (graphs) rendered
-// with gnuplot.
+// This controller serves real time data for plots (graphs) rendered
+// with Open Flash Chart.
 function controller_plot($suburl) {
     switch ($suburl) {
         case 'rating':
@@ -26,7 +26,7 @@
                 'user' => $user,
             );
 
-            // output gnuplot
+            // output data for Open Flash Chart
             execute_view_die('views/plot_rating.php', $view);
 
         case 'distribution':
@@ -46,7 +46,7 @@
             //
             // Note: This bucket size is relative to the absolute ratings
             // ranging from ~1000 to ~2500
-            $bucket_size = 20;
+            $bucket_size = 60;
             $distribution = rating_distribution($bucket_size);
 
             // view
@@ -56,8 +56,9 @@
                 'user' => $user,
             );
 
-            // output gnuplot
+            // output data for Open Flash Chart
             execute_view_die('views/plot_distribution.php', $view);
+
         default:
             flash('Actiunea nu este valida.');
             redirect(url_home());
Index: www/macros/macro_distributionchart.php
===================================================================
--- www/macros/macro_distributionchart.php	(revision 0)
+++ www/macros/macro_distributionchart.php	(revision 0)
@@ -0,0 +1,26 @@
+<?php
+
+//  This macro is used to display a chart with the distribution of the rating
+//  and the position of a user.
+//
+//  Arguments:
+//      user (required)
+//
+//  Example:
+//      ==DistributionChart(user="gigel")==
+function macro_distributionchart($args) {
+    $user = getattr($args, 'user');
+
+    if (is_null($user)) {
+        return macro_error('Expected argument `user`');
+    }
+
+    $html = '<br /><div id="distribution-chart"></div><br />'
+    . '<script type="text/javascript"> swfobject.embedSWF("' . html_escape(url_static("swf/open-flash-chart.swf"))
+    . '", "distribution-chart", "560", "280", "9.0.0", null, {"data-file":"' . html_escape(url_home() . 'plot/distribution?user=' . $user) . '"}); </script>';
+
+    return $html;
+}
+
+?>
+
Index: www/macros/macro_ratingchart.php
===================================================================
--- www/macros/macro_ratingchart.php	(revision 0)
+++ www/macros/macro_ratingchart.php	(revision 0)
@@ -0,0 +1,25 @@
+<?php
+
+//  This macro is used to display a chart with the rating evolution of a user.
+//
+//  Arguments:
+//      user (required)
+//
+//  Example:
+//      ==RatingChart(user="gigel")==
+function macro_ratingchart($args) {
+    $user = getattr($args, 'user');
+
+    if (is_null($user)) {
+        return macro_error('Expected argument `user`');
+    }
+
+    $html = '<br /><div id="rating-chart"></div><br />'
+    . '<script type="text/javascript"> swfobject.embedSWF("' . html_escape(url_static("swf/open-flash-chart.swf"))
+    . '", "rating-chart", "560", "280", "9.0.0", null, {"data-file":"' . html_escape(url_home() . 'plot/rating?user=' . $user) . '"}); </script>';
+
+    return $html;
+}
+
+?>
+
Index: www/php-ofc-library/JSON.php
===================================================================
--- www/php-ofc-library/JSON.php	(revision 0)
+++ www/php-ofc-library/JSON.php	(revision 0)
@@ -0,0 +1,806 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Converts to and from JSON format.
+ *
+ * JSON (JavaScript Object Notation) is a lightweight data-interchange
+ * format. It is easy for humans to read and write. It is easy for machines
+ * to parse and generate. It is based on a subset of the JavaScript
+ * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
+ * This feature can also be found in  Python. JSON is a text format that is
+ * completely language independent but uses conventions that are familiar
+ * to programmers of the C-family of languages, including C, C++, C#, Java,
+ * JavaScript, Perl, TCL, and many others. These properties make JSON an
+ * ideal data-interchange language.
+ *
+ * This package provides a simple encoder and decoder for JSON notation. It
+ * is intended for use with client-side Javascript applications that make
+ * use of HTTPRequest to perform server communication functions - data can
+ * be encoded into JSON notation for use in a client-side javascript, or
+ * decoded from incoming Javascript requests. JSON format is native to
+ * Javascript, and can be directly eval()'ed with no further parsing
+ * overhead
+ *
+ * All strings should be in ASCII or UTF-8 format!
+ *
+ * LICENSE: Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met: Redistributions of source code must retain the
+ * above copyright notice, this list of conditions and the following
+ * disclaimer. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * @category
+ * @package     Services_JSON
+ * @author      Michal Migurski <mike-json@teczno.com>
+ * @author      Matt Knapp <mdknapp[at]gmail[dot]com>
+ * @author      Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
+ * @copyright   2005 Michal Migurski
+ * @version     CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
+ * @license     http://www.opensource.org/licenses/bsd-license.php
+ * @link        http://pear.php.net/pepr/pepr-proposal-show.php?id=198
+ */
+
+/**
+ * Marker constant for Services_JSON::decode(), used to flag stack state
+ */
+define('SERVICES_JSON_SLICE',   1);
+
+/**
+ * Marker constant for Services_JSON::decode(), used to flag stack state
+ */
+define('SERVICES_JSON_IN_STR',  2);
+
+/**
+ * Marker constant for Services_JSON::decode(), used to flag stack state
+ */
+define('SERVICES_JSON_IN_ARR',  3);
+
+/**
+ * Marker constant for Services_JSON::decode(), used to flag stack state
+ */
+define('SERVICES_JSON_IN_OBJ',  4);
+
+/**
+ * Marker constant for Services_JSON::decode(), used to flag stack state
+ */
+define('SERVICES_JSON_IN_CMT', 5);
+
+/**
+ * Behavior switch for Services_JSON::decode()
+ */
+define('SERVICES_JSON_LOOSE_TYPE', 16);
+
+/**
+ * Behavior switch for Services_JSON::decode()
+ */
+define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
+
+/**
+ * Converts to and from JSON format.
+ *
+ * Brief example of use:
+ *
+ * <code>
+ * // create a new instance of Services_JSON
+ * $json = new Services_JSON();
+ *
+ * // convert a complexe value to JSON notation, and send it to the browser
+ * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
+ * $output = $json->encode($value);
+ *
+ * print($output);
+ * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
+ *
+ * // accept incoming POST data, assumed to be in JSON notation
+ * $input = file_get_contents('php://input', 1000000);
+ * $value = $json->decode($input);
+ * </code>
+ */
+class Services_JSON
+{
+   /**
+    * constructs a new JSON instance
+    *
+    * @param    int     $use    object behavior flags; combine with boolean-OR
+    *
+    *                           possible values:
+    *                           - SERVICES_JSON_LOOSE_TYPE:  loose typing.
+    *                                   "{...}" syntax creates associative arrays
+    *                                   instead of objects in decode().
+    *                           - SERVICES_JSON_SUPPRESS_ERRORS:  error suppression.
+    *                                   Values which can't be encoded (e.g. resources)
+    *                                   appear as NULL instead of throwing errors.
+    *                                   By default, a deeply-nested resource will
+    *                                   bubble up with an error, so all return values
+    *                                   from encode() should be checked with isError()
+    */
+    function Services_JSON($use = 0)
+    {
+        $this->use = $use;
+    }
+
+   /**
+    * convert a string from one UTF-16 char to one UTF-8 char
+    *
+    * Normally should be handled by mb_convert_encoding, but
+    * provides a slower PHP-only method for installations
+    * that lack the multibye string extension.
+    *
+    * @param    string  $utf16  UTF-16 character
+    * @return   string  UTF-8 character
+    * @access   private
+    */
+    function utf162utf8($utf16)
+    {
+        // oh please oh please oh please oh please oh please
+        if(function_exists('mb_convert_encoding')) {
+            return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
+        }
+
+        $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
+
+        switch(true) {
+            case ((0x7F & $bytes) == $bytes):
+                // this case should never be reached, because we are in ASCII range
+                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                return chr(0x7F & $bytes);
+
+            case (0x07FF & $bytes) == $bytes:
+                // return a 2-byte UTF-8 character
+                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                return chr(0xC0 | (($bytes >> 6) & 0x1F))
+                     . chr(0x80 | ($bytes & 0x3F));
+
+            case (0xFFFF & $bytes) == $bytes:
+                // return a 3-byte UTF-8 character
+                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                return chr(0xE0 | (($bytes >> 12) & 0x0F))
+                     . chr(0x80 | (($bytes >> 6) & 0x3F))
+                     . chr(0x80 | ($bytes & 0x3F));
+        }
+
+        // ignoring UTF-32 for now, sorry
+        return '';
+    }
+
+   /**
+    * convert a string from one UTF-8 char to one UTF-16 char
+    *
+    * Normally should be handled by mb_convert_encoding, but
+    * provides a slower PHP-only method for installations
+    * that lack the multibye string extension.
+    *
+    * @param    string  $utf8   UTF-8 character
+    * @return   string  UTF-16 character
+    * @access   private
+    */
+    function utf82utf16($utf8)
+    {
+        // oh please oh please oh please oh please oh please
+        if(function_exists('mb_convert_encoding')) {
+            return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
+        }
+
+        switch(strlen($utf8)) {
+            case 1:
+                // this case should never be reached, because we are in ASCII range
+                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                return $utf8;
+
+            case 2:
+                // return a UTF-16 character from a 2-byte UTF-8 char
+                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                return chr(0x07 & (ord($utf8{0}) >> 2))
+                     . chr((0xC0 & (ord($utf8{0}) << 6))
+                         | (0x3F & ord($utf8{1})));
+
+            case 3:
+                // return a UTF-16 character from a 3-byte UTF-8 char
+                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                return chr((0xF0 & (ord($utf8{0}) << 4))
+                         | (0x0F & (ord($utf8{1}) >> 2)))
+                     . chr((0xC0 & (ord($utf8{1}) << 6))
+                         | (0x7F & ord($utf8{2})));
+        }
+
+        // ignoring UTF-32 for now, sorry
+        return '';
+    }
+
+   /**
+    * encodes an arbitrary variable into JSON format
+    *
+    * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
+    *                           see argument 1 to Services_JSON() above for array-parsing behavior.
+    *                           if var is a strng, note that encode() always expects it
+    *                           to be in ASCII or UTF-8 format!
+    *
+    * @return   mixed   JSON string representation of input var or an error if a problem occurs
+    * @access   public
+    */
+    function encode($var)
+    {
+        switch (gettype($var)) {
+            case 'boolean':
+                return $var ? 'true' : 'false';
+
+            case 'NULL':
+                return 'null';
+
+            case 'integer':
+                return (int) $var;
+
+            case 'double':
+            case 'float':
+                return (float) $var;
+
+            case 'string':
+                // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
+                $ascii = '';
+                $strlen_var = strlen($var);
+
+               /*
+                * Iterate over every character in the string,
+                * escaping with a slash or encoding to UTF-8 where necessary
+                */
+                for ($c = 0; $c < $strlen_var; ++$c) {
+
+                    $ord_var_c = ord($var{$c});
+
+                    switch (true) {
+                        case $ord_var_c == 0x08:
+                            $ascii .= '\b';
+                            break;
+                        case $ord_var_c == 0x09:
+                            $ascii .= '\t';
+                            break;
+                        case $ord_var_c == 0x0A:
+                            $ascii .= '\n';
+                            break;
+                        case $ord_var_c == 0x0C:
+                            $ascii .= '\f';
+                            break;
+                        case $ord_var_c == 0x0D:
+                            $ascii .= '\r';
+                            break;
+
+                        case $ord_var_c == 0x22:
+                        case $ord_var_c == 0x2F:
+                        case $ord_var_c == 0x5C:
+                            // double quote, slash, slosh
+                            $ascii .= '\\'.$var{$c};
+                            break;
+
+                        case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
+                            // characters U-00000000 - U-0000007F (same as ASCII)
+                            $ascii .= $var{$c};
+                            break;
+
+                        case (($ord_var_c & 0xE0) == 0xC0):
+                            // characters U-00000080 - U-000007FF, mask 110XXXXX
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
+                            $c += 1;
+                            $utf16 = $this->utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+
+                        case (($ord_var_c & 0xF0) == 0xE0):
+                            // characters U-00000800 - U-0000FFFF, mask 1110XXXX
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c,
+                                         ord($var{$c + 1}),
+                                         ord($var{$c + 2}));
+                            $c += 2;
+                            $utf16 = $this->utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+
+                        case (($ord_var_c & 0xF8) == 0xF0):
+                            // characters U-00010000 - U-001FFFFF, mask 11110XXX
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c,
+                                         ord($var{$c + 1}),
+                                         ord($var{$c + 2}),
+                                         ord($var{$c + 3}));
+                            $c += 3;
+                            $utf16 = $this->utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+
+                        case (($ord_var_c & 0xFC) == 0xF8):
+                            // characters U-00200000 - U-03FFFFFF, mask 111110XX
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c,
+                                         ord($var{$c + 1}),
+                                         ord($var{$c + 2}),
+                                         ord($var{$c + 3}),
+                                         ord($var{$c + 4}));
+                            $c += 4;
+                            $utf16 = $this->utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+
+                        case (($ord_var_c & 0xFE) == 0xFC):
+                            // characters U-04000000 - U-7FFFFFFF, mask 1111110X
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c,
+                                         ord($var{$c + 1}),
+                                         ord($var{$c + 2}),
+                                         ord($var{$c + 3}),
+                                         ord($var{$c + 4}),
+                                         ord($var{$c + 5}));
+                            $c += 5;
+                            $utf16 = $this->utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+                    }
+                }
+
+                return '"'.$ascii.'"';
+
+            case 'array':
+               /*
+                * As per JSON spec if any array key is not an integer
+                * we must treat the the whole array as an object. We
+                * also try to catch a sparsely populated associative
+                * array with numeric keys here because some JS engines
+                * will create an array with empty indexes up to
+                * max_index which can cause memory issues and because
+                * the keys, which may be relevant, will be remapped
+                * otherwise.
+                *
+                * As per the ECMA and JSON specification an object may
+                * have any string as a property. Unfortunately due to
+                * a hole in the ECMA specification if the key is a
+                * ECMA reserved word or starts with a digit the
+                * parameter is only accessible using ECMAScript's
+                * bracket notation.
+                */
+
+                // treat as a JSON object
+                if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
+                    $properties = array_map(array($this, 'name_value'),
+                                            array_keys($var),
+                                            array_values($var));
+
+                    foreach($properties as $property) {
+                        if(Services_JSON::isError($property)) {
+                            return $property;
+                        }
+                    }
+
+                    return '{' . join(',', $properties) . '}';
+                }
+
+                // treat it like a regular array
+                $elements = array_map(array($this, 'encode'), $var);
+
+                foreach($elements as $element) {
+                    if(Services_JSON::isError($element)) {
+                        return $element;
+                    }
+                }
+
+                return '[' . join(',', $elements) . ']';
+
+            case 'object':
+                $vars = get_object_vars($var);
+
+                $properties = array_map(array($this, 'name_value'),
+                                        array_keys($vars),
+                                        array_values($vars));
+
+                foreach($properties as $property) {
+                    if(Services_JSON::isError($property)) {
+                        return $property;
+                    }
+                }
+
+                return '{' . join(',', $properties) . '}';
+
+            default:
+                return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
+                    ? 'null'
+                    : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
+        }
+    }
+
+   /**
+    * array-walking function for use in generating JSON-formatted name-value pairs
+    *
+    * @param    string  $name   name of key to use
+    * @param    mixed   $value  reference to an array element to be encoded
+    *
+    * @return   string  JSON-formatted name-value pair, like '"name":value'
+    * @access   private
+    */
+    function name_value($name, $value)
+    {
+        $encoded_value = $this->encode($value);
+
+        if(Services_JSON::isError($encoded_value)) {
+            return $encoded_value;
+        }
+
+        return $this->encode(strval($name)) . ':' . $encoded_value;
+    }
+
+   /**
+    * reduce a string by removing leading and trailing comments and whitespace
+    *
+    * @param    $str    string      string value to strip of comments and whitespace
+    *
+    * @return   string  string value stripped of comments and whitespace
+    * @access   private
+    */
+    function reduce_string($str)
+    {
+        $str = preg_replace(array(
+
+                // eliminate single line comments in '// ...' form
+                '#^\s*//(.+)$#m',
+
+                // eliminate multi-line comments in '/* ... */' form, at start of string
+                '#^\s*/\*(.+)\*/#Us',
+
+                // eliminate multi-line comments in '/* ... */' form, at end of string
+                '#/\*(.+)\*/\s*$#Us'
+
+            ), '', $str);
+
+        // eliminate extraneous space
+        return trim($str);
+    }
+
+   /**
+    * decodes a JSON string into appropriate variable
+    *
+    * @param    string  $str    JSON-formatted string
+    *
+    * @return   mixed   number, boolean, string, array, or object
+    *                   corresponding to given JSON input string.
+    *                   See argument 1 to Services_JSON() above for object-output behavior.
+    *                   Note that decode() always returns strings
+    *                   in ASCII or UTF-8 format!
+    * @access   public
+    */
+    function decode($str)
+    {
+        $str = $this->reduce_string($str);
+
+        switch (strtolower($str)) {
+            case 'true':
+                return true;
+
+            case 'false':
+                return false;
+
+            case 'null':
+                return null;
+
+            default:
+                $m = array();
+
+                if (is_numeric($str)) {
+                    // Lookie-loo, it's a number
+
+                    // This would work on its own, but I'm trying to be
+                    // good about returning integers where appropriate:
+                    // return (float)$str;
+
+                    // Return float or int, as appropriate
+                    return ((float)$str == (integer)$str)
+                        ? (integer)$str
+                        : (float)$str;
+
+                } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
+                    // STRINGS RETURNED IN UTF-8 FORMAT
+                    $delim = substr($str, 0, 1);
+                    $chrs = substr($str, 1, -1);
+                    $utf8 = '';
+                    $strlen_chrs = strlen($chrs);
+
+                    for ($c = 0; $c < $strlen_chrs; ++$c) {
+
+                        $substr_chrs_c_2 = substr($chrs, $c, 2);
+                        $ord_chrs_c = ord($chrs{$c});
+
+                        switch (true) {
+                            case $substr_chrs_c_2 == '\b':
+                                $utf8 .= chr(0x08);
+                                ++$c;
+                                break;
+                            case $substr_chrs_c_2 == '\t':
+                                $utf8 .= chr(0x09);
+                                ++$c;
+                                break;
+                            case $substr_chrs_c_2 == '\n':
+                                $utf8 .= chr(0x0A);
+                                ++$c;
+                                break;
+                            case $substr_chrs_c_2 == '\f':
+                                $utf8 .= chr(0x0C);
+                                ++$c;
+                                break;
+                            case $substr_chrs_c_2 == '\r':
+                                $utf8 .= chr(0x0D);
+                                ++$c;
+                                break;
+
+                            case $substr_chrs_c_2 == '\\"':
+                            case $substr_chrs_c_2 == '\\\'':
+                            case $substr_chrs_c_2 == '\\\\':
+                            case $substr_chrs_c_2 == '\\/':
+                                if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
+                                   ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
+                                    $utf8 .= $chrs{++$c};
+                                }
+                                break;
+
+                            case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
+                                // single, escaped unicode character
+                                $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
+                                       . chr(hexdec(substr($chrs, ($c + 4), 2)));
+                                $utf8 .= $this->utf162utf8($utf16);
+                                $c += 5;
+                                break;
+
+                            case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
+                                $utf8 .= $chrs{$c};
+                                break;
+
+                            case ($ord_chrs_c & 0xE0) == 0xC0:
+                                // characters U-00000080 - U-000007FF, mask 110XXXXX
+                                //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                                $utf8 .= substr($chrs, $c, 2);
+                                ++$c;
+                                break;
+
+                            case ($ord_chrs_c & 0xF0) == 0xE0:
+                                // characters U-00000800 - U-0000FFFF, mask 1110XXXX
+                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                                $utf8 .= substr($chrs, $c, 3);
+                                $c += 2;
+                                break;
+
+                            case ($ord_chrs_c & 0xF8) == 0xF0:
+                                // characters U-00010000 - U-001FFFFF, mask 11110XXX
+                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                                $utf8 .= substr($chrs, $c, 4);
+                                $c += 3;
+                                break;
+
+                            case ($ord_chrs_c & 0xFC) == 0xF8:
+                                // characters U-00200000 - U-03FFFFFF, mask 111110XX
+                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                                $utf8 .= substr($chrs, $c, 5);
+                                $c += 4;
+                                break;
+
+                            case ($ord_chrs_c & 0xFE) == 0xFC:
+                                // characters U-04000000 - U-7FFFFFFF, mask 1111110X
+                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                                $utf8 .= substr($chrs, $c, 6);
+                                $c += 5;
+                                break;
+
+                        }
+
+                    }
+
+                    return $utf8;
+
+                } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
+                    // array, or object notation
+
+                    if ($str{0} == '[') {
+                        $stk = array(SERVICES_JSON_IN_ARR);
+                        $arr = array();
+                    } else {
+                        if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
+                            $stk = array(SERVICES_JSON_IN_OBJ);
+                            $obj = array();
+                        } else {
+                            $stk = array(SERVICES_JSON_IN_OBJ);
+                            $obj = new stdClass();
+                        }
+                    }
+
+                    array_push($stk, array('what'  => SERVICES_JSON_SLICE,
+                                           'where' => 0,
+                                           'delim' => false));
+
+                    $chrs = substr($str, 1, -1);
+                    $chrs = $this->reduce_string($chrs);
+
+                    if ($chrs == '') {
+                        if (reset($stk) == SERVICES_JSON_IN_ARR) {
+                            return $arr;
+
+                        } else {
+                            return $obj;
+
+                        }
+                    }
+
+                    //print("\nparsing {$chrs}\n");
+
+                    $strlen_chrs = strlen($chrs);
+
+                    for ($c = 0; $c <= $strlen_chrs; ++$c) {
+
+                        $top = end($stk);
+                        $substr_chrs_c_2 = substr($chrs, $c, 2);
+
+                        if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
+                            // found a comma that is not inside a string, array, etc.,
+                            // OR we've reached the end of the character list
+                            $slice = substr($chrs, $top['where'], ($c - $top['where']));
+                            array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
+                            //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+                            if (reset($stk) == SERVICES_JSON_IN_ARR) {
+                                // we are in an array, so just push an element onto the stack
+                                array_push($arr, $this->decode($slice));
+
+                            } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
+                                // we are in an object, so figure
+                                // out the property name and set an
+                                // element in an associative array,
+                                // for now
+                                $parts = array();
+                                
+                                if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
+                                    // "name":value pair
+                                    $key = $this->decode($parts[1]);
+                                    $val = $this->decode($parts[2]);
+
+                                    if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
+                                        $obj[$key] = $val;
+                                    } else {
+                                        $obj->$key = $val;
+                                    }
+                                } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
+                                    // name:value pair, where name is unquoted
+                                    $key = $parts[1];
+                                    $val = $this->decode($parts[2]);
+
+                                    if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
+                                        $obj[$key] = $val;
+                                    } else {
+                                        $obj->$key = $val;
+                                    }
+                                }
+
+                            }
+
+                        } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
+                            // found a quote, and we are not inside a string
+                            array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
+                            //print("Found start of string at {$c}\n");
+
+                        } elseif (($chrs{$c} == $top['delim']) &&
+                                 ($top['what'] == SERVICES_JSON_IN_STR) &&
+                                 ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
+                            // found a quote, we're in a string, and it's not escaped
+                            // we know that it's not escaped becase there is _not_ an
+                            // odd number of backslashes at the end of the string so far
+                            array_pop($stk);
+                            //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
+
+                        } elseif (($chrs{$c} == '[') &&
+                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
+                            // found a left-bracket, and we are in an array, object, or slice
+                            array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
+                            //print("Found start of array at {$c}\n");
+
+                        } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
+                            // found a right-bracket, and we're in an array
+                            array_pop($stk);
+                            //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+                        } elseif (($chrs{$c} == '{') &&
+                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
+                            // found a left-brace, and we are in an array, object, or slice
+                            array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
+                            //print("Found start of object at {$c}\n");
+
+                        } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
+                            // found a right-brace, and we're in an object
+                            array_pop($stk);
+                            //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+                        } elseif (($substr_chrs_c_2 == '/*') &&
+                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
+                            // found a comment start, and we are in an array, object, or slice
+                            array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
+                            $c++;
+                            //print("Found start of comment at {$c}\n");
+
+                        } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
+                            // found a comment end, and we're in one now
+                            array_pop($stk);
+                            $c++;
+
+                            for ($i = $top['where']; $i <= $c; ++$i)
+                                $chrs = substr_replace($chrs, ' ', $i, 1);
+
+                            //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+                        }
+
+                    }
+
+                    if (reset($stk) == SERVICES_JSON_IN_ARR) {
+                        return $arr;
+
+                    } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
+                        return $obj;
+
+                    }
+
+                }
+        }
+    }
+
+    /**
+     * @todo Ultimately, this should just call PEAR::isError()
+     */
+    function isError($data, $code = null)
+    {
+        if (class_exists('pear')) {
+            return PEAR::isError($data, $code);
+        } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
+                                 is_subclass_of($data, 'services_json_error'))) {
+            return true;
+        }
+
+        return false;
+    }
+}
+
+if (class_exists('PEAR_Error')) {
+
+    class Services_JSON_Error extends PEAR_Error
+    {
+        function Services_JSON_Error($message = 'unknown error', $code = null,
+                                     $mode = null, $options = null, $userinfo = null)
+        {
+            parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
+        }
+    }
+
+} else {
+
+    /**
+     * @todo Ultimately, this class shall be descended from PEAR_Error
+     */
+    class Services_JSON_Error
+    {
+        function Services_JSON_Error($message = 'unknown error', $code = null,
+                                     $mode = null, $options = null, $userinfo = null)
+        {
+
+        }
+    }
+
+}
+    
+?>
Index: www/php-ofc-library/README.txt
===================================================================
--- www/php-ofc-library/README.txt	(revision 0)
+++ www/php-ofc-library/README.txt	(revision 0)
@@ -0,0 +1,16 @@
+﻿Open Flash Chart - PHP libraries. These help create data files for Open Flash Chart.
+Copyright (C) 2007 
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
\ No newline at end of file
Index: www/php-ofc-library/json_format.php
===================================================================
--- www/php-ofc-library/json_format.php	(revision 0)
+++ www/php-ofc-library/json_format.php	(revision 0)
@@ -0,0 +1,86 @@
+<?php
+
+// Pretty print some JSON
+function json_format($json)
+{
+    $tab = "  ";
+    $new_json = "";
+    $indent_level = 0;
+    $in_string = false;
+
+/*
+ commented out by monk.e.boy 22nd May '08
+ because my web server is PHP4, and
+ json_* are PHP5 functions...
+
+    $json_obj = json_decode($json);
+
+    if($json_obj === false)
+        return false;
+
+    $json = json_encode($json_obj);
+*/
+    $len = strlen($json);
+
+    for($c = 0; $c < $len; $c++)
+    {
+        $char = $json[$c];
+        switch($char)
+        {
+            case '{':
+            case '[':
+                if(!$in_string)
+                {
+                    $new_json .= $char . "\n" . str_repeat($tab, $indent_level+1);
+                    $indent_level++;
+                }
+                else
+                {
+                    $new_json .= $char;
+                }
+                break;
+            case '}':
+            case ']':
+                if(!$in_string)
+                {
+                    $indent_level--;
+                    $new_json .= "\n" . str_repeat($tab, $indent_level) . $char;
+                }
+                else
+                {
+                    $new_json .= $char;
+                }
+                break;
+            case ',':
+                if(!$in_string)
+                {
+                    $new_json .= ",\n" . str_repeat($tab, $indent_level);
+                }
+                else
+                {
+                    $new_json .= $char;
+                }
+                break;
+            case ':':
+                if(!$in_string)
+                {
+                    $new_json .= ": ";
+                }
+                else
+                {
+                    $new_json .= $char;
+                }
+                break;
+            case '"':
+                if($c > 0 && $json[$c-1] != '\\')
+                {
+                    $in_string = !$in_string;
+                }
+            default:
+                $new_json .= $char;
+                break;                   
+        }
+    }
+
+    return $new_json;
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_area_base.php
===================================================================
--- www/php-ofc-library/ofc_area_base.php	(revision 0)
+++ www/php-ofc-library/ofc_area_base.php	(revision 0)
@@ -0,0 +1,66 @@
+<?php
+
+class area_base
+{
+	function area_base()
+	{
+		$tmp = 'fill-alpha';
+		$this->$tmp = 0.35;		
+		$this->values    = array();	
+	}
+	
+	function set_width( $w )
+	{
+		$this->width     = $w;
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+	
+	function set_fill_colour( $colour )
+	{
+		$this->fill = $colour;
+	}
+	
+	function set_fill_alpha( $alpha )
+	{
+		$tmp = "fill-alpha";
+		$this->$tmp = $alpha;
+	}
+	
+	function set_halo_size( $size )
+	{
+		$tmp = 'halo-size';
+		$this->$tmp = $size;
+	}
+	
+	function set_values( $v )
+	{
+		$this->values = $v;		
+	}
+	
+	function set_dot_size( $size )
+	{
+		$tmp = 'dot-size';
+		$this->$tmp = $size;
+	}
+
+	function set_key( $text, $font_size )
+	{
+		$this->text      = $text;
+		$tmp = 'font-size';
+		$this->$tmp = $font_size;
+	}
+	
+	function set_tooltip( $tip )
+	{
+		$this->tip = $tip;	
+	}
+	
+	function set_loop()
+	{
+		$this->loop = true;
+	}
+}
Index: www/php-ofc-library/ofc_area_hollow.php
===================================================================
--- www/php-ofc-library/ofc_area_hollow.php	(revision 0)
+++ www/php-ofc-library/ofc_area_hollow.php	(revision 0)
@@ -0,0 +1,10 @@
+<?php
+
+class area_hollow extends area_base
+{
+	function area_hollow()
+	{
+		$this->type      = "area_hollow";
+		parent::area_base();
+	}
+}
Index: www/php-ofc-library/ofc_area_line.php
===================================================================
--- www/php-ofc-library/ofc_area_line.php	(revision 0)
+++ www/php-ofc-library/ofc_area_line.php	(revision 0)
@@ -0,0 +1,10 @@
+<?php
+
+class area_line extends area_base
+{
+	function area_line()
+	{
+		$this->type      = "area_line";
+		parent::area_base();
+	}
+}
Index: www/php-ofc-library/ofc_bar.php
===================================================================
--- www/php-ofc-library/ofc_bar.php	(revision 0)
+++ www/php-ofc-library/ofc_bar.php	(revision 0)
@@ -0,0 +1,34 @@
+<?php
+
+include_once 'ofc_bar_base.php';
+
+class bar_value
+{
+	function bar_value( $top, $bottom=null )
+	{
+		$this->top = $top;
+		
+		if( isset( $bottom ) )
+			$this->bottom = $bottom;
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+	
+	function set_tooltip( $tip )
+	{
+		$this->tip = $tip;
+	}
+}
+
+class bar extends bar_base
+{
+	function bar()
+	{
+		$this->type      = "bar";
+		parent::bar_base();
+	}
+}
+
Index: www/php-ofc-library/ofc_bar_3d.php
===================================================================
--- www/php-ofc-library/ofc_bar_3d.php	(revision 0)
+++ www/php-ofc-library/ofc_bar_3d.php	(revision 0)
@@ -0,0 +1,31 @@
+<?php
+
+include_once 'ofc_bar_base.php';
+
+class bar_3d_value
+{
+	function bar_3d_value( $top )
+	{
+		$this->top = $top;
+//		$this->bottom = $bottom;
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+	
+	function set_tooltip( $tip )
+	{
+		$this->tip = $tip;
+	}
+}
+
+class bar_3d extends bar_base
+{
+	function bar_3d()
+	{
+		$this->type      = "bar_3d";
+		parent::bar_base();
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_bar_base.php
===================================================================
--- www/php-ofc-library/ofc_bar_base.php	(revision 0)
+++ www/php-ofc-library/ofc_bar_base.php	(revision 0)
@@ -0,0 +1,41 @@
+<?php
+
+/* this is a base class */
+
+class bar_base
+{
+	function bar_base(){}
+
+	function set_key( $text, $size )
+	{
+		$this->text = $text;
+		$tmp = 'font-size';
+		$this->$tmp = $size;
+	}
+
+	function set_values( $v )
+	{
+		$this->values = $v;		
+	}
+	
+	function append_value( $v )
+	{
+		$this->values[] = $v;		
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;	
+	}
+
+	function set_alpha( $alpha )
+	{
+		$this->alpha = $alpha;	
+	}
+	
+	function set_tooltip( $tip )
+	{
+		$this->tip = $tip;	
+	}
+}
+
Index: www/php-ofc-library/ofc_bar_filled.php
===================================================================
--- www/php-ofc-library/ofc_bar_filled.php	(revision 0)
+++ www/php-ofc-library/ofc_bar_filled.php	(revision 0)
@@ -0,0 +1,39 @@
+<?php
+
+include_once 'ofc_bar_base.php';
+
+class bar_filled_value extends bar_value
+{
+	function bar_filled_value( $top, $bottom=null )
+	{
+		parent::bar_value( $top, $bottom );	
+	}
+	
+	function set_outline_colour( $outline_colour )
+	{
+		$tmp = 'outline-colour';
+		$this->$tmp = $outline_colour;	
+	}
+}
+
+class bar_filled extends bar_base
+{
+	function bar_filled( $colour=null, $outline_colour=null )
+	{
+		$this->type      = "bar_filled";
+		parent::bar_base();
+		
+		if( isset( $colour ) )
+			$this->set_colour( $colour );
+		
+		if( isset( $outline_colour ) )
+			$this->set_outline_colour( $outline_colour );
+	}
+	
+	function set_outline_colour( $outline_colour )
+	{
+		$tmp = 'outline-colour';
+		$this->$tmp = $outline_colour;	
+	}
+}
+
Index: www/php-ofc-library/ofc_bar_glass.php
===================================================================
--- www/php-ofc-library/ofc_bar_glass.php	(revision 0)
+++ www/php-ofc-library/ofc_bar_glass.php	(revision 0)
@@ -0,0 +1,33 @@
+<?php
+
+include_once 'ofc_bar_base.php';
+
+
+class bar_glass_value
+{
+	function bar_glass_value( $top )
+	{
+		$this->top = $top;
+//		$this->bottom = $bottom;
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+	
+	function set_tooltip( $tip )
+	{
+		$this->tip = $tip;
+	}
+}
+
+
+class bar_glass extends bar_base
+{
+	function bar_glass()
+	{
+		$this->type      = "bar_glass";
+		parent::bar_base();
+	}
+}
Index: www/php-ofc-library/ofc_bar_sketch.php
===================================================================
--- www/php-ofc-library/ofc_bar_sketch.php	(revision 0)
+++ www/php-ofc-library/ofc_bar_sketch.php	(revision 0)
@@ -0,0 +1,23 @@
+<?php
+
+include_once 'ofc_bar_base.php';
+
+class bar_sketch extends bar_base
+{
+	function bar_sketch( $colour, $outline_colour, $fun_factor )
+	{
+		$this->type      = "bar_sketch";
+		parent::bar_base();
+		
+		$this->set_colour( $colour );
+		$this->set_outline_colour( $outline_colour );
+		$this->offset = $fun_factor;
+	}
+	
+	function set_outline_colour( $outline_colour )
+	{
+		$tmp = 'outline-colour';
+		$this->$tmp = $outline_colour;	
+	}
+}
+
Index: www/php-ofc-library/ofc_bar_stack.php
===================================================================
--- www/php-ofc-library/ofc_bar_stack.php	(revision 0)
+++ www/php-ofc-library/ofc_bar_stack.php	(revision 0)
@@ -0,0 +1,50 @@
+<?php
+
+include_once 'ofc_bar_base.php';
+
+class bar_stack extends bar_base
+{
+	function bar_stack()
+	{
+		$this->type      = "bar_stack";
+		parent::bar_base();
+	}
+	
+	function append_stack( $v )
+	{
+		$this->append_value( $v );
+	}
+	
+	// an array of HEX colours strings
+	// e.g. array( '#ff0000', '#00ff00' );
+	function set_colours( $colours )
+	{
+		$this->colours = $colours;
+	}
+	
+	// an array of bar_stack_value
+	function set_keys( $keys )
+	{
+		$this->keys = $keys;
+	}
+}
+
+class bar_stack_value
+{
+	function bar_stack_value( $val, $colour )
+	{
+		$this->val = $val;
+		$this->colour = $colour;
+	}
+}
+
+class bar_stack_key
+{
+	function bar_stack_key( $colour, $text, $font_size )
+	{
+		$this->colour = $colour;
+		$this->text = $text;
+		$tmp = 'font-size';
+		$this->$tmp = $font_size;
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_hbar.php
===================================================================
--- www/php-ofc-library/ofc_hbar.php	(revision 0)
+++ www/php-ofc-library/ofc_hbar.php	(revision 0)
@@ -0,0 +1,64 @@
+<?php
+
+class hbar_value
+{
+	function hbar_value( $left, $right=null )
+	{
+		if( isset( $right ) )
+		{
+			$this->left = $left;
+			$this->right = $right;
+		}
+		else
+			$this->right = $left;
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;	
+	}
+	
+	function set_tooltip( $tip )
+	{
+		$this->tip = $tip;	
+	}
+}
+
+class hbar
+{
+	function hbar( $colour )
+	{
+		$this->type      = "hbar";
+		$this->values    = array();
+		$this->set_colour( $colour );
+	}
+	
+	function append_value( $v )
+	{
+		$this->values[] = $v;		
+	}
+	
+	function set_values( $v )
+	{
+		foreach( $v as $val )
+			$this->append_value( new hbar_value( $val ) );
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;	
+	}
+	
+	function set_key( $text, $size )
+	{
+		$this->text = $text;
+		$tmp = 'font-size';
+		$this->$tmp = $size;
+	}
+	
+	function set_tooltip( $tip )
+	{
+		$this->tip = $tip;	
+	}
+}
+
Index: www/php-ofc-library/ofc_line.php
===================================================================
--- www/php-ofc-library/ofc_line.php	(revision 0)
+++ www/php-ofc-library/ofc_line.php	(revision 0)
@@ -0,0 +1,9 @@
+<?php
+
+class line extends line_base
+{
+	function line()
+	{
+		$this->type      = "line";
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_line_base.php
===================================================================
--- www/php-ofc-library/ofc_line_base.php	(revision 0)
+++ www/php-ofc-library/ofc_line_base.php	(revision 0)
@@ -0,0 +1,70 @@
+<?php
+
+class line_base
+{
+	function line_base()
+	{
+		$this->type      = "line_dot";
+		$this->text      = "Page views";
+		$tmp = 'font-size';
+		$this->$tmp = 10;
+		
+		$this->values    = array(9,6,7,9,5,7,6,9,7);
+	}
+	
+	function set_values( $v )
+	{
+		$this->values = $v;		
+	}
+	
+	function set_width( $width )
+	{
+		$this->width = $width;		
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+	
+	function set_dot_size( $size )
+	{
+		$tmp = 'dot-size';
+		$this->$tmp = $size;		
+	}
+	
+	function set_halo_size( $size )
+	{
+		$tmp = 'halo-size';
+		$this->$tmp = $size;		
+	}
+	
+	function set_key( $text, $font_size )
+	{
+		$this->text      = $text;
+		$tmp = 'font-size';
+		$this->$tmp = $font_size;
+	}
+	
+	function set_tooltip( $tip )
+	{
+		$this->tip = $tip;
+	}
+	
+	function set_on_click( $text )
+	{
+		$tmp = 'on-click';
+		$this->$tmp = $text;
+	}
+	
+	function loop()
+	{
+		$this->loop = true;
+	}
+	
+	function line_style( $s )
+	{
+		$tmp = "line-style";
+		$this->$tmp = $s;
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_line_dot.php
===================================================================
--- www/php-ofc-library/ofc_line_dot.php	(revision 0)
+++ www/php-ofc-library/ofc_line_dot.php	(revision 0)
@@ -0,0 +1,33 @@
+<?php
+
+class dot_value
+{
+	function dot_value( $value, $colour )
+	{
+		$this->value = $value;
+		$this->colour = $colour;
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+	
+	function set_size( $size )
+	{
+		$this->size = $size;
+	}
+	
+	function set_tooltip( $tip )
+	{
+		$this->tip = $tip;
+	}
+}
+
+class line_dot extends line_base
+{
+	function line_dot()
+	{
+		$this->type      = "line_dot";
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_line_hollow.php
===================================================================
--- www/php-ofc-library/ofc_line_hollow.php	(revision 0)
+++ www/php-ofc-library/ofc_line_hollow.php	(revision 0)
@@ -0,0 +1,9 @@
+<?php
+
+class line_hollow extends line_base
+{
+	function line_hollow()
+	{
+		$this->type      = "line_hollow";
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_line_style.php
===================================================================
--- www/php-ofc-library/ofc_line_style.php	(revision 0)
+++ www/php-ofc-library/ofc_line_style.php	(revision 0)
@@ -0,0 +1,11 @@
+<?php
+
+class line_style
+{
+	function line_style($on, $off)
+	{
+		$this->style	= "dash";
+		$this->on		= $on;
+		$this->off		= $off;
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_pie.php
===================================================================
--- www/php-ofc-library/ofc_pie.php	(revision 0)
+++ www/php-ofc-library/ofc_pie.php	(revision 0)
@@ -0,0 +1,109 @@
+<?php
+
+class pie_value
+{
+	function pie_value( $value, $label )
+	{
+		$this->value = $value;
+		$this->label = $label;
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+	
+	function set_label( $label, $label_colour, $font_size )
+	{
+		$this->label = $label;
+		
+		$tmp = 'label-colour';
+		$this->$tmp = $label_colour;
+		
+		$tmp = 'font-size';
+		$this->$tmp = $font_size;
+		
+	}
+	
+	function set_tooltip( $tip )
+	{
+		$this->tip = $tip;
+	}
+	
+	function on_click( $event )
+	{
+		$tmp = 'on-click';
+		$this->$tmp = $event;
+	}
+
+}
+
+class pie
+{
+	function pie()
+	{
+		$this->type      		= 'pie';
+		$this->colours     		= array("#d01f3c","#356aa0","#C79810");
+		$this->border			= 2;
+	}
+	
+	function set_colours( $colours )
+	{
+		$this->colours = $colours;
+	}
+	
+	function set_alpha( $alpha )
+	{
+		$this->alpha = $alpha;
+	}
+	
+	function set_values( $v )
+	{
+		$this->values = $v;		
+	}
+	
+	// boolean
+	function set_animate( $animate )
+	{
+		$this->animate = $animate;
+	}
+	
+	// real
+	function set_start_angle( $angle )
+	{
+		$tmp = 'start-angle';
+		$this->$tmp = $angle;
+	}
+	
+	function set_tooltip( $tip )
+	{
+		$this->tip = $tip;
+	}
+	
+	function set_gradient_fill()
+	{
+		$tmp = 'gradient-fill';
+		$this->$tmp = true;
+	}
+	
+	function set_label_colour( $label_colour )
+	{
+		$tmp = 'label-colour';
+		$this->$tmp = $label_colour;	
+	}
+	
+	/**
+	 * Turn off the labels
+	 */
+	function set_no_labels()
+	{
+		$tmp = 'no-labels';
+		$this->$tmp = true;
+	}
+	
+	function on_click( $event )
+	{
+		$tmp = 'on-click';
+		$this->$tmp = $event;
+	}
+}
Index: www/php-ofc-library/ofc_radar_axis.php
===================================================================
--- www/php-ofc-library/ofc_radar_axis.php	(revision 0)
+++ www/php-ofc-library/ofc_radar_axis.php	(revision 0)
@@ -0,0 +1,47 @@
+<?php
+
+class radar_axis
+{
+	function radar_axis( $max )
+	{
+		$this->set_max( $max );
+	}
+	
+	function set_max( $max )
+	{
+		$this->max = $max;
+	}
+	
+	function set_steps( $steps )
+	{
+		$this->steps = $steps;
+	}
+	
+	function set_stroke( $s )
+	{
+		$this->stroke = $s;
+	}
+    
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+	
+	function set_grid_colour( $colour )
+	{
+		$tmp = 'grid-colour';
+		$this->$tmp = $colour;
+	}
+	
+	function set_labels( $labels )
+	{
+		$this->labels = $labels;
+	}
+	
+	function set_spoke_labels( $labels )
+	{
+		$tmp = 'spoke-labels';
+		$this->$tmp = $labels;
+	}
+}
+
Index: www/php-ofc-library/ofc_radar_axis_labels.php
===================================================================
--- www/php-ofc-library/ofc_radar_axis_labels.php	(revision 0)
+++ www/php-ofc-library/ofc_radar_axis_labels.php	(revision 0)
@@ -0,0 +1,15 @@
+<?php
+
+class radar_axis_labels
+{
+	// $labels : array
+	function radar_axis_labels( $labels )
+	{
+		$this->labels = $labels;
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_radar_spoke_labels.php
===================================================================
--- www/php-ofc-library/ofc_radar_spoke_labels.php	(revision 0)
+++ www/php-ofc-library/ofc_radar_spoke_labels.php	(revision 0)
@@ -0,0 +1,15 @@
+<?php
+
+class radar_spoke_labels
+{
+	// $labels : array
+	function radar_spoke_labels( $labels )
+	{
+		$this->labels = $labels;
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_scatter.php
===================================================================
--- www/php-ofc-library/ofc_scatter.php	(revision 0)
+++ www/php-ofc-library/ofc_scatter.php	(revision 0)
@@ -0,0 +1,42 @@
+<?php
+
+class scatter_value
+{
+	function scatter_value( $x, $y, $dot_size=-1 )
+	{
+		$this->x = $x;
+		$this->y = $y;
+		
+		if( $dot_size > 0 )
+		{
+			$tmp = 'dot-size';
+			$this->$tmp = $dot_size;
+		}
+	}
+}
+
+class scatter
+{
+	function scatter( $colour, $dot_size )
+	{
+		$this->type      = "scatter";
+		$this->set_colour( $colour );
+		$this->set_dot_size( $dot_size );
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+
+	function set_dot_size( $dot_size )
+	{
+		$tmp = 'dot-size';
+		$this->$tmp = $dot_size;
+	}
+	
+	function set_values( $values )
+	{
+		$this->values = $values;
+	}
+}
Index: www/php-ofc-library/ofc_scatter_line.php
===================================================================
--- www/php-ofc-library/ofc_scatter_line.php	(revision 0)
+++ www/php-ofc-library/ofc_scatter_line.php	(revision 0)
@@ -0,0 +1,27 @@
+<?php
+
+class scatter_line
+{
+	function scatter_line( $colour, $dot_size )
+	{
+		$this->type      = "scatter_line";
+		$this->set_colour( $colour );
+		$this->set_dot_size( $dot_size );
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+
+	function set_dot_size( $dot_size )
+	{
+		$tmp = 'dot-size';
+		$this->$tmp = $dot_size;
+	}
+	
+	function set_values( $values )
+	{
+		$this->values = $values;
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_shape.php
===================================================================
--- www/php-ofc-library/ofc_shape.php	(revision 0)
+++ www/php-ofc-library/ofc_shape.php	(revision 0)
@@ -0,0 +1,25 @@
+<?php
+
+class shape_point
+{
+	function shape_point( $x, $y )
+	{
+		$this->x = $x;
+		$this->y = $y;
+	}
+}
+
+class shape
+{
+	function shape( $colour )
+	{
+		$this->type		= "shape";
+		$this->colour	= $colour;
+		$this->values	= array();
+	}
+	
+	function append_value( $p )
+	{
+		$this->values[] = $p;	
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_title.php
===================================================================
--- www/php-ofc-library/ofc_title.php	(revision 0)
+++ www/php-ofc-library/ofc_title.php	(revision 0)
@@ -0,0 +1,15 @@
+<?php
+
+class title
+{
+	function title( $text='' )
+	{
+		$this->text = $text;
+	}
+	
+	function set_style( $css )
+	{
+		$this->style = $css;
+		//"{font-size: 20px; color:#0000ff; font-family: Verdana; text-align: center;}";		
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_tooltip.php
===================================================================
--- www/php-ofc-library/ofc_tooltip.php	(revision 0)
+++ www/php-ofc-library/ofc_tooltip.php	(revision 0)
@@ -0,0 +1,51 @@
+<?php
+
+include_once 'ofc_bar_base.php';
+
+class tooltip
+{
+	function tooltip(){}
+	
+	function set_shadow( $shadow )
+	{
+		$this->shadow = $shadow;
+	}
+	
+	// stroke in pixels (e.g. 5 )
+	function set_stroke( $stroke )
+	{
+		$this->stroke = $stroke;
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+	
+	function set_background_colour( $bg )
+	{
+		$this->background = $bg;
+	}
+	
+	// a css style
+	function set_title_style( $style )
+	{
+		$this->title = $style;
+	}
+	
+    function set_body_style( $style )
+	{
+		$this->body = $style;
+	}
+	
+	function set_proximity()
+	{
+		$this->mouse = 1;
+	}
+	
+	function set_hover()
+	{
+		$this->mouse = 2;
+	}
+}
+
Index: www/php-ofc-library/ofc_upload_image.php
===================================================================
--- www/php-ofc-library/ofc_upload_image.php	(revision 0)
+++ www/php-ofc-library/ofc_upload_image.php	(revision 0)
@@ -0,0 +1,61 @@
+<?php
+
+//
+// In Open Flash Chart -> save_image debug mode, you
+// will see the 'echo' text in a new window.
+//
+
+/*
+ 
+print_r( $_GET );
+print_r( $_POST );
+print_r( $_FILES );
+
+print_r( $GLOBALS );
+print_r( $GLOBALS["HTTP_RAW_POST_DATA"] );
+
+*/
+
+
+// default path for the image to be stored //
+$default_path = '../tmp-upload-images/';
+
+if (!file_exists($default_path)) mkdir($default_path, 0777, true);
+
+// full path to the saved image including filename //
+$destination = $default_path . basename( $_GET[ 'name' ] ); 
+
+echo 'Saving your image to: '. $destination;
+
+$jfh = fopen($destination, 'w') or die("can't open file");
+fwrite($jfh, $GLOBALS['HTTP_RAW_POST_DATA']);
+fclose($jfh);
+
+//
+// LOOK:
+//
+exit();
+
+
+//
+// PHP5:
+//
+
+
+// default path for the image to be stored //
+$default_path = 'tmp-upload-images/';
+
+if (!file_exists($default_path)) mkdir($default_path, 0777, true);
+
+// full path to the saved image including filename //
+$destination = $default_path . basename( $_FILES[ 'Filedata' ][ 'name' ] ); 
+
+// move the image into the specified directory //
+if (move_uploaded_file($_FILES[ 'Filedata' ][ 'tmp_name' ], $destination)) {
+    echo "The file " . basename( $_FILES[ 'Filedata' ][ 'name' ] ) . " has been uploaded;";
+} else {
+    echo "FILE UPLOAD FAILED";
+}
+
+
+?>
Index: www/php-ofc-library/ofc_x_axis.php
===================================================================
--- www/php-ofc-library/ofc_x_axis.php	(revision 0)
+++ www/php-ofc-library/ofc_x_axis.php	(revision 0)
@@ -0,0 +1,77 @@
+<?php
+
+class x_axis
+{
+	function x_axis(){}
+
+	function set_stroke( $stroke )
+	{
+		$this->stroke = $stroke;	
+	}
+	
+	function set_colours( $colour, $grid_colour )
+	{
+		$this->set_colour( $colour );
+		$this->set_grid_colour( $grid_colour );
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;	
+	}
+	
+	function set_tick_height( $height )
+	{
+		$tmp = 'tick-height';
+		$this->$tmp      		= $height;
+	}
+	
+	function set_grid_colour( $colour )
+	{
+		$tmp = 'grid-colour';
+		$this->$tmp = $colour;
+	}
+	
+	// $o is a boolean
+	function set_offset( $o )
+	{
+		$this->offset = $o?true:false;	
+	}
+	
+	function set_steps( $steps )
+	{
+		$this->steps = $steps;
+	}
+	
+	function set_3d( $val )
+	{
+		$tmp = '3d';
+		$this->$tmp				= $val;		
+	}
+	
+	function set_labels( $x_axis_labels )
+	{
+		//$this->labels = $v;
+		$this->labels = $x_axis_labels;
+	}
+	
+	//
+	// helper function to make the examples
+	// simpler.
+	//
+	function set_labels_from_array( $a )
+	{
+		$x_axis_labels = new x_axis_labels();
+		$x_axis_labels->set_labels( $a );
+		$this->labels = $x_axis_labels;
+		
+		if( isset( $this->steps ) )
+			$x_axis_labels->set_steps( $this->steps );
+	}
+	
+	function set_range( $min, $max )
+	{
+		$this->min = $min;
+		$this->max = $max;
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_x_axis_label.php
===================================================================
--- www/php-ofc-library/ofc_x_axis_label.php	(revision 0)
+++ www/php-ofc-library/ofc_x_axis_label.php	(revision 0)
@@ -0,0 +1,42 @@
+<?php
+
+class x_axis_label
+{
+	function x_axis_label( $text, $colour, $size, $rotate )
+	{
+		$this->set_text( $text );
+		$this->set_colour( $colour );
+		$this->set_size( $size );
+		$this->set_rotate( $rotate );
+	}
+	
+	function set_text( $text )
+	{
+		$this->text = $text;
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+	
+	function set_size( $size )
+	{
+		$this->size = $size;
+	}
+	
+	function set_rotate( $rotate )
+	{
+		$this->rotate = $rotate;
+	}
+	
+	function set_vertical()
+	{
+		$this->rotate = "vertical";
+	}
+	
+	function set_visible()
+	{
+		$this->visible = true;
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_x_axis_labels.php
===================================================================
--- www/php-ofc-library/ofc_x_axis_labels.php	(revision 0)
+++ www/php-ofc-library/ofc_x_axis_labels.php	(revision 0)
@@ -0,0 +1,34 @@
+<?php
+
+class x_axis_labels
+{
+	function x_axis_labels(){}
+	
+	function set_steps( $steps )
+	{
+		$this->steps = $steps;
+	}
+	
+	//
+	// An array of [x_axis_label or string]
+	//
+	function set_labels( $labels )
+	{
+		$this->labels = $labels;
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+	
+	function set_size( $size )
+	{
+		$this->size = $size;
+	}
+	
+	function set_vertical()
+	{
+		$this->rotate = "vertical";
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_x_legend.php
===================================================================
--- www/php-ofc-library/ofc_x_legend.php	(revision 0)
+++ www/php-ofc-library/ofc_x_legend.php	(revision 0)
@@ -0,0 +1,15 @@
+<?php
+
+class x_legend
+{
+	function x_legend( $text='' )
+	{
+		$this->text = $text;
+	}
+	
+	function set_style( $css )
+	{
+		$this->style = $css;
+		//"{font-size: 20px; color:#0000ff; font-family: Verdana; text-align: center;}";		
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_y_axis.php
===================================================================
--- www/php-ofc-library/ofc_y_axis.php	(revision 0)
+++ www/php-ofc-library/ofc_y_axis.php	(revision 0)
@@ -0,0 +1,17 @@
+<?php
+
+class y_axis extends y_axis_base
+{
+	function y_axis(){}
+	
+	//
+	// y axis right does NOT control
+	// grid colour, the left axis does
+	//
+	function set_grid_colour( $colour )
+	{
+		$tmp = 'grid-colour';
+		$this->$tmp = $colour;
+	}
+	
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_y_axis_base.php
===================================================================
--- www/php-ofc-library/ofc_y_axis_base.php	(revision 0)
+++ www/php-ofc-library/ofc_y_axis_base.php	(revision 0)
@@ -0,0 +1,56 @@
+<?php
+
+class y_axis_base
+{
+	function y_axis_base(){}
+	
+	function set_stroke( $s )
+	{
+		$this->stroke = $s;
+	}
+	
+	function set_tick_length( $val )
+	{
+		$tmp = 'tick-length';
+		$this->$tmp = $val;
+	}
+	
+	function set_colours( $colour, $grid_colour )
+	{
+		$this->set_colour( $colour );
+		$this->set_grid_colour( $grid_colour );
+	}
+	
+	function set_colour( $colour )
+	{
+		$this->colour = $colour;
+	}
+	
+	function set_grid_colour( $colour )
+	{
+		$tmp = 'grid-colour';
+		$this->$tmp = $colour;
+	}
+	
+	function set_range( $min, $max, $steps=1 )
+	{
+		$this->min = $min;
+		$this->max = $max;
+		$this->set_steps( $steps );
+	}
+	
+	function set_offset( $off )
+	{
+		$this->offset = $off?1:0;
+	}
+	
+	function set_labels( $labels )
+	{
+		$this->labels = $labels;	
+	}
+	
+	function set_steps( $steps )
+	{
+		$this->steps = $steps;	
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_y_axis_right.php
===================================================================
--- www/php-ofc-library/ofc_y_axis_right.php	(revision 0)
+++ www/php-ofc-library/ofc_y_axis_right.php	(revision 0)
@@ -0,0 +1,6 @@
+<?php
+
+class y_axis_right extends y_axis_base
+{
+	function y_axis_right(){}
+}
\ No newline at end of file
Index: www/php-ofc-library/ofc_y_legend.php
===================================================================
--- www/php-ofc-library/ofc_y_legend.php	(revision 0)
+++ www/php-ofc-library/ofc_y_legend.php	(revision 0)
@@ -0,0 +1,15 @@
+<?php
+
+class y_legend
+{
+	function y_legend( $text='' )
+	{
+		$this->text = $text;
+	}
+	
+	function set_style( $css )
+	{
+		$this->style = $css;
+		//"{font-size: 20px; color:#0000ff; font-family: Verdana; text-align: center;}";		
+	}
+}
\ No newline at end of file
Index: www/php-ofc-library/open-flash-chart-object.php
===================================================================
--- www/php-ofc-library/open-flash-chart-object.php	(revision 0)
+++ www/php-ofc-library/open-flash-chart-object.php	(revision 0)
@@ -0,0 +1,109 @@
+<?php
+
+function open_flash_chart_object_str( $width, $height, $url, $use_swfobject=true, $base='' )
+{
+    //
+    // return the HTML as a string
+    //
+    return _ofc( $width, $height, $url, $use_swfobject, $base );
+}
+
+function open_flash_chart_object( $width, $height, $url, $use_swfobject=true, $base='' )
+{
+    //
+    // stream the HTML into the page
+    //
+    echo _ofc( $width, $height, $url, $use_swfobject, $base );
+}
+
+function _ofc( $width, $height, $url, $use_swfobject, $base )
+{
+    //
+    // I think we may use swfobject for all browsers,
+    // not JUST for IE...
+    //
+    //$ie = strstr(getenv('HTTP_USER_AGENT'), 'MSIE');
+    
+    //
+    // escape the & and stuff:
+    //
+    $url = urlencode($url);
+    
+    //
+    // output buffer
+    //
+    $out = array();
+    
+    //
+    // check for http or https:
+    //
+    if (isset ($_SERVER['HTTPS']))
+    {
+        if (strtoupper ($_SERVER['HTTPS']) == 'ON')
+        {
+            $protocol = 'https';
+        }
+        else
+        {
+            $protocol = 'http';
+        }
+    }
+    else
+    {
+        $protocol = 'http';
+    }
+    
+    //
+    // if there are more than one charts on the
+    // page, give each a different ID
+    //
+    global $open_flash_chart_seqno;
+    $obj_id = 'chart';
+    $div_name = 'flashcontent';
+    
+    //$out[] = '<script type="text/javascript" src="'. $base .'js/ofc.js"></script>';
+    
+    if( !isset( $open_flash_chart_seqno ) )
+    {
+        $open_flash_chart_seqno = 1;
+        $out[] = '<script type="text/javascript" src="'. $base .'js/swfobject.js"></script>';
+    }
+    else
+    {
+        $open_flash_chart_seqno++;
+        $obj_id .= '_'. $open_flash_chart_seqno;
+        $div_name .= '_'. $open_flash_chart_seqno;
+    }
+    
+    if( $use_swfobject )
+    {
+		// Using library for auto-enabling Flash object on IE, disabled-Javascript proof  
+		$out[] = '<div id="'. $div_name .'"></div>';
+		$out[] = '<script type="text/javascript">';
+		$out[] = 'var so = new SWFObject("'. $base .'open-flash-chart.swf", "'. $obj_id .'", "'. $width . '", "' . $height . '", "9", "#FFFFFF");';
+		
+		$out[] = 'so.addVariable("data-file", "'. $url . '");';
+	
+		$out[] = 'so.addParam("allowScriptAccess", "always" );//"sameDomain");';
+		$out[] = 'so.write("'. $div_name .'");';
+		$out[] = '</script>';
+		$out[] = '<noscript>';
+    }
+
+    $out[] = '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="' . $protocol . '://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" ';
+    $out[] = 'width="' . $width . '" height="' . $height . '" id="ie_'. $obj_id .'" align="middle">';
+    $out[] = '<param name="allowScriptAccess" value="sameDomain" />';
+    $out[] = '<param name="movie" value="'. $base .'open-flash-chart.swf?data='. $url .'" />';
+    $out[] = '<param name="quality" value="high" />';
+    $out[] = '<param name="bgcolor" value="#FFFFFF" />';
+    $out[] = '<embed src="'. $base .'open-flash-chart.swf?data=' . $url .'" quality="high" bgcolor="#FFFFFF" width="'. $width .'" height="'. $height .'" name="'. $obj_id .'" align="middle" allowScriptAccess="sameDomain" ';
+    $out[] = 'type="application/x-shockwave-flash" pluginspage="' . $protocol . '://www.macromedia.com/go/getflashplayer" id="'. $obj_id .'"/>';
+    $out[] = '</object>';
+
+    if ( $use_swfobject ) {
+		$out[] = '</noscript>';
+    }
+    
+    return implode("\n",$out);
+}
+?>
\ No newline at end of file
Index: www/php-ofc-library/open-flash-chart.php
===================================================================
--- www/php-ofc-library/open-flash-chart.php	(revision 0)
+++ www/php-ofc-library/open-flash-chart.php	(revision 0)
@@ -0,0 +1,138 @@
+<?php
+
+// var_dump(debug_backtrace());
+
+//
+// Omar Kilani's php C extension for encoding JSON has been incorporated in stock PHP since 5.2.0
+// http://www.aurore.net/projects/php-json/
+//
+// -- Marcus Engene
+//
+if (! function_exists('json_encode'))
+{
+	include_once 'JSON.php';
+}
+
+include_once 'json_format.php';
+
+// ofc classes
+include_once 'ofc_title.php';
+include_once 'ofc_y_axis_base.php';
+include_once 'ofc_y_axis.php';
+include_once 'ofc_y_axis_right.php';
+include_once 'ofc_x_axis.php';
+include_once 'ofc_area_base.php';
+include_once 'ofc_area_hollow.php';
+include_once 'ofc_area_line.php';
+include_once 'ofc_pie.php';
+include_once 'ofc_bar.php';
+include_once 'ofc_bar_filled.php';
+include_once 'ofc_bar_glass.php';
+include_once 'ofc_bar_stack.php';
+include_once 'ofc_bar_3d.php';
+include_once 'ofc_hbar.php';
+include_once 'ofc_line_base.php';
+include_once 'ofc_line.php';
+include_once 'ofc_line_dot.php';
+include_once 'ofc_line_hollow.php';
+include_once 'ofc_x_legend.php';
+include_once 'ofc_y_legend.php';
+include_once 'ofc_bar_sketch.php';
+include_once 'ofc_scatter.php';
+include_once 'ofc_scatter_line.php';
+include_once 'ofc_x_axis_labels.php';
+include_once 'ofc_x_axis_label.php';
+include_once 'ofc_tooltip.php';
+include_once 'ofc_shape.php';
+include_once 'ofc_radar_axis.php';
+include_once 'ofc_radar_axis_labels.php';
+include_once 'ofc_radar_spoke_labels.php';
+include_once 'ofc_line_style.php';
+
+class open_flash_chart
+{
+	function open_flash_chart()
+	{
+		//$this->title = new title( "Many data lines" );
+		$this->elements = array();
+	}
+	
+	function set_title( $t )
+	{
+		$this->title = $t;
+	}
+	
+	function set_x_axis( $x )
+	{
+		$this->x_axis = $x;	
+	}
+	
+	function set_y_axis( $y )
+	{
+		$this->y_axis = $y;
+	}
+	
+	function add_y_axis( $y )
+	{
+		$this->y_axis = $y;
+	}
+
+	function set_y_axis_right( $y )
+	{
+		$this->y_axis_right = $y;
+	}
+	
+	function add_element( $e )
+	{
+		$this->elements[] = $e;
+	}
+	
+	function set_x_legend( $x )
+	{
+		$this->x_legend = $x;
+	}
+
+	function set_y_legend( $y )
+	{
+		$this->y_legend = $y;
+	}
+	
+	function set_bg_colour( $colour )
+	{
+		$this->bg_colour = $colour;	
+	}
+	
+	function set_radar_axis( $radar )
+	{
+		$this->radar_axis = $radar;
+	}
+	
+	function set_tooltip( $tooltip )
+	{
+		$this->tooltip = $tooltip;	
+	}
+	
+	function toString()
+	{
+		if (function_exists('json_encode'))
+		{
+			return json_encode($this);
+		}
+		else
+		{
+			$json = new Services_JSON();
+			return $json->encode( $this );
+		}
+	}
+	
+	function toPrettyString()
+	{
+		return json_format( $this->toString() );
+	}
+}
+
+
+
+//
+// there is no PHP end tag so we don't mess the headers up!
+//
\ No newline at end of file
Index: www/static/js/json/json2.js
===================================================================
--- www/static/js/json/json2.js	(revision 0)
+++ www/static/js/json/json2.js	(revision 0)
@@ -0,0 +1,461 @@
+/*
+    http://www.JSON.org/json2.js
+    2008-03-24
+
+    Public Domain.
+
+    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+    See http://www.JSON.org/js.html
+
+    This file creates a global JSON object containing three methods: stringify,
+    parse, and quote.
+
+
+        JSON.stringify(value, replacer, space)
+            value       any JavaScript value, usually an object or array.
+
+            replacer    an optional parameter that determines how object
+                        values are stringified for objects without a toJSON
+                        method. It can be a function or an array.
+
+            space       an optional parameter that specifies the indentation
+                        of nested structures. If it is omitted, the text will
+                        be packed without extra whitespace. If it is a number,
+                        it will specify the number of spaces to indent at each
+                        level. If it is a string (such as '\t'), it contains the
+                        characters used to indent at each level.
+
+            This method produces a JSON text from a JavaScript value.
+
+            When an object value is found, if the object contains a toJSON
+            method, its toJSON method will be called and the result will be
+            stringified. A toJSON method does not serialize: it returns the
+            value represented by the name/value pair that should be serialized,
+            or undefined if nothing should be serialized. The toJSON method will
+            be passed the key associated with the value, and this will be bound
+            to the object holding the key.
+
+            This is the toJSON method added to Dates:
+
+                function toJSON(key) {
+                    return this.getUTCFullYear()   + '-' +
+                         f(this.getUTCMonth() + 1) + '-' +
+                         f(this.getUTCDate())      + 'T' +
+                         f(this.getUTCHours())     + ':' +
+                         f(this.getUTCMinutes())   + ':' +
+                         f(this.getUTCSeconds())   + 'Z';
+                }
+
+            You can provide an optional replacer method. It will be passed the
+            key and value of each member, with this bound to the containing
+            object. The value that is returned from your method will be
+            serialized. If your method returns undefined, then the member will
+            be excluded from the serialization.
+
+            If no replacer parameter is provided, then a default replacer
+            will be used:
+
+                function replacer(key, value) {
+                    return Object.hasOwnProperty.call(this, key) ?
+                        value : undefined;
+                }
+
+            The default replacer is passed the key and value for each item in
+            the structure. It excludes inherited members.
+
+            If the replacer parameter is an array, then it will be used to
+            select the members to be serialized. It filters the results such
+            that only members with keys listed in the replacer array are
+            stringified.
+
+            Values that do not have JSON representaions, such as undefined or
+            functions, will not be serialized. Such values in objects will be
+            dropped; in arrays they will be replaced with null. You can use
+            a replacer function to replace those with JSON values.
+            JSON.stringify(undefined) returns undefined.
+
+            The optional space parameter produces a stringification of the value
+            that is filled with line breaks and indentation to make it easier to
+            read.
+
+            If the space parameter is a non-empty string, then that string will
+            be used for indentation. If the space parameter is a number, then
+            then indentation will be that many spaces.
+
+            Example:
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}]);
+            // text is '["e",{"pluribus":"unum"}]'
+
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+
+        JSON.parse(text, reviver)
+            This method parses a JSON text to produce an object or array.
+            It can throw a SyntaxError exception.
+
+            The optional reviver parameter is a function that can filter and
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
+
+            Example:
+
+            // Parse the text. Values that look like ISO date strings will
+            // be converted to Date objects.
+
+            myData = JSON.parse(text, function (key, value) {
+                var a;
+                if (typeof value === 'string') {
+                    a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+                    if (a) {
+                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+                            +a[5], +a[6]));
+                    }
+                }
+                return value;
+            });
+
+
+        JSON.quote(text)
+            This method wraps a string in quotes, escaping some characters
+            as needed.
+
+
+    This is a reference implementation. You are free to copy, modify, or
+    redistribute.
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD THIRD PARTY
+    CODE INTO YOUR PAGES.
+*/
+
+/*jslint regexp: true, forin: true, evil: true */
+
+/*global JSON */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+    call, charCodeAt, floor, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, length,
+    parse, propertyIsEnumerable, prototype, push, quote, replace, stringify,
+    test, toJSON, toString
+*/
+
+if (!this.JSON) {
+
+// Create a JSON object only if one does not already exist. We create the
+// object in a closure to avoid global variables.
+
+    JSON = function () {
+
+        function f(n) {    // Format integers to have at least two digits.
+            return n < 10 ? '0' + n : n;
+        }
+
+        Date.prototype.toJSON = function () {
+
+// Eventually, this method will be based on the date.toISOString method.
+
+            return this.getUTCFullYear()   + '-' +
+                 f(this.getUTCMonth() + 1) + '-' +
+                 f(this.getUTCDate())      + 'T' +
+                 f(this.getUTCHours())     + ':' +
+                 f(this.getUTCMinutes())   + ':' +
+                 f(this.getUTCSeconds())   + 'Z';
+        };
+
+
+        var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
+            gap,
+            indent,
+            meta = {    // table of character substitutions
+                '\b': '\\b',
+                '\t': '\\t',
+                '\n': '\\n',
+                '\f': '\\f',
+                '\r': '\\r',
+                '"' : '\\"',
+                '\\': '\\\\'
+            },
+            rep;
+
+
+        function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+            return escapeable.test(string) ?
+                '"' + string.replace(escapeable, function (a) {
+                    var c = meta[a];
+                    if (typeof c === 'string') {
+                        return c;
+                    }
+                    c = a.charCodeAt();
+                    return '\\u00' + Math.floor(c / 16).toString(16) +
+                                               (c % 16).toString(16);
+                }) + '"' :
+                '"' + string + '"';
+        }
+
+
+        function str(key, holder) {
+
+// Produce a string from holder[key].
+
+            var i,          // The loop counter.
+                k,          // The member key.
+                v,          // The member value.
+                length,
+                mind = gap,
+                partial,
+                value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+            if (value && typeof value === 'object' &&
+                    typeof value.toJSON === 'function') {
+                value = value.toJSON(key);
+            }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+            if (typeof rep === 'function') {
+                value = rep.call(holder, key, value);
+            }
+
+// What happens next depends on the value's type.
+
+            switch (typeof value) {
+            case 'string':
+                return quote(value);
+
+            case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+                return isFinite(value) ? String(value) : 'null';
+
+            case 'boolean':
+            case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+                return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+            case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+                if (!value) {
+                    return 'null';
+                }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+                gap += indent;
+                partial = [];
+
+// If the object has a dontEnum length property, we'll treat it as an array.
+
+                if (typeof value.length === 'number' &&
+                        !(value.propertyIsEnumerable('length'))) {
+
+// The object is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                    length = value.length;
+                    for (i = 0; i < length; i += 1) {
+                        partial[i] = str(i, value) || 'null';
+                    }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+                    v = partial.length === 0 ? '[]' :
+                        gap ? '[\n' + gap + partial.join(',\n' + gap) +
+                                  '\n' + mind + ']' :
+                              '[' + partial.join(',') + ']';
+                    gap = mind;
+                    return v;
+                }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+                if (typeof rep === 'object') {
+                    length = rep.length;
+                    for (i = 0; i < length; i += 1) {
+                        k = rep[i];
+                        if (typeof k === 'string') {
+                            v = str(k, value, rep);
+                            if (v) {
+                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                            }
+                        }
+                    }
+                } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                    for (k in value) {
+                        v = str(k, value, rep);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+                v = partial.length === 0 ? '{}' :
+                    gap ? '{\n' + gap + partial.join(',\n' + gap) +
+                              '\n' + mind + '}' :
+                          '{' + partial.join(',') + '}';
+                gap = mind;
+                return v;
+            }
+        }
+
+
+// Return the JSON object containing the stringify, parse, and quote methods.
+
+        return {
+            stringify: function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+                var i;
+                gap = '';
+                indent = '';
+                if (space) {
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+                    if (typeof space === 'number') {
+                        for (i = 0; i < space; i += 1) {
+                            indent += ' ';
+                        }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+                    } else if (typeof space === 'string') {
+                        indent = space;
+                    }
+                }
+
+// If there is no replacer parameter, use the default replacer.
+
+                if (!replacer) {
+                    rep = function (key, value) {
+                        if (!Object.hasOwnProperty.call(this, key)) {
+                            return undefined;
+                        }
+                        return value;
+                    };
+
+// The replacer can be a function or an array. Otherwise, throw an error.
+
+                } else if (typeof replacer === 'function' ||
+                        (typeof replacer === 'object' &&
+                         typeof replacer.length === 'number')) {
+                    rep = replacer;
+                } else {
+                    throw new Error('JSON.stringify');
+                }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+                return str('', {'': value});
+            },
+
+
+            parse: function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+                var j;
+
+                function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+                    var k, v, value = holder[key];
+                    if (value && typeof value === 'object') {
+                        for (k in value) {
+                            if (Object.hasOwnProperty.call(value, k)) {
+                                v = walk(value, k);
+                                if (v !== undefined) {
+                                    value[k] = v;
+                                } else {
+                                    delete value[k];
+                                }
+                            }
+                        }
+                    }
+                    return reviver.call(holder, key, value);
+                }
+
+
+// Parsing happens in three stages. In the first stage, we run the text against
+// regular expressions that look for non-JSON patterns. We are especially
+// concerned with '()' and 'new' because they can cause invocation, and '='
+// because it can cause mutation. But just to be safe, we want to reject all
+// unexpected forms.
+
+// We split the first stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace all backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+                if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
+replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the second stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                    j = eval('(' + text + ')');
+
+// In the optional third stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+                    return typeof reviver === 'function' ?
+                        walk({'': j}, '') : j;
+                }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+                throw new SyntaxError('JSON.parse');
+            },
+
+            quote: quote
+        };
+    }();
+}
Index: www/static/js/swfobject.js
===================================================================
--- www/static/js/swfobject.js	(revision 0)
+++ www/static/js/swfobject.js	(revision 0)
@@ -0,0 +1,5 @@
+/*	SWFObject v2.0 <http://code.google.com/p/swfobject/>
+	Copyright (c) 2007 Geoff Stearns, Michael Williams, and Bobby van der Sluis
+	This software is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
+*/
+var swfobject=function(){var Z="undefined",P="object",B="Shockwave Flash",h="ShockwaveFlash.ShockwaveFlash",W="application/x-shockwave-flash",K="SWFObjectExprInst",G=window,g=document,N=navigator,f=[],H=[],Q=null,L=null,T=null,S=false,C=false;var a=function(){var l=typeof g.getElementById!=Z&&typeof g.getElementsByTagName!=Z&&typeof g.createElement!=Z&&typeof g.appendChild!=Z&&typeof g.replaceChild!=Z&&typeof g.removeChild!=Z&&typeof g.cloneNode!=Z,t=[0,0,0],n=null;if(typeof N.plugins!=Z&&typeof N.plugins[B]==P){n=N.plugins[B].description;if(n){n=n.replace(/^.*\s+(\S+\s+\S+$)/,"$1");t[0]=parseInt(n.replace(/^(.*)\..*$/,"$1"),10);t[1]=parseInt(n.replace(/^.*\.(.*)\s.*$/,"$1"),10);t[2]=/r/.test(n)?parseInt(n.replace(/^.*r(.*)$/,"$1"),10):0}}else{if(typeof G.ActiveXObject!=Z){var o=null,s=false;try{o=new ActiveXObject(h+".7")}catch(k){try{o=new ActiveXObject(h+".6");t=[6,0,21];o.AllowScriptAccess="always"}catch(k){if(t[0]==6){s=true}}if(!s){try{o=new ActiveXObject(h)}catch(k){}}}if(!s&&o){try{n=o.GetVariable("$version");if(n){n=n.split(" ")[1].split(",");t=[parseInt(n[0],10),parseInt(n[1],10),parseInt(n[2],10)]}}catch(k){}}}}var v=N.userAgent.toLowerCase(),j=N.platform.toLowerCase(),r=/webkit/.test(v)?parseFloat(v.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,i=false,q=j?/win/.test(j):/win/.test(v),m=j?/mac/.test(j):/mac/.test(v);/*@cc_on i=true;@if(@_win32)q=true;@elif(@_mac)m=true;@end@*/return{w3cdom:l,pv:t,webkit:r,ie:i,win:q,mac:m}}();var e=function(){if(!a.w3cdom){return }J(I);if(a.ie&&a.win){try{g.write("<script id=__ie_ondomload defer=true src=//:><\/script>");var i=c("__ie_ondomload");if(i){i.onreadystatechange=function(){if(this.readyState=="complete"){this.parentNode.removeChild(this);V()}}}}catch(j){}}if(a.webkit&&typeof g.readyState!=Z){Q=setInterval(function(){if(/loaded|complete/.test(g.readyState)){V()}},10)}if(typeof g.addEventListener!=Z){g.addEventListener("DOMContentLoaded",V,null)}M(V)}();function V(){if(S){return }if(a.ie&&a.win){var m=Y("span");try{var l=g.getElementsByTagName("body")[0].appendChild(m);l.parentNode.removeChild(l)}catch(n){return }}S=true;if(Q){clearInterval(Q);Q=null}var j=f.length;for(var k=0;k<j;k++){f[k]()}}function J(i){if(S){i()}else{f[f.length]=i}}function M(j){if(typeof G.addEventListener!=Z){G.addEventListener("load",j,false)}else{if(typeof g.addEventListener!=Z){g.addEventListener("load",j,false)}else{if(typeof G.attachEvent!=Z){G.attachEvent("onload",j)}else{if(typeof G.onload=="function"){var i=G.onload;G.onload=function(){i();j()}}else{G.onload=j}}}}}function I(){var l=H.length;for(var j=0;j<l;j++){var m=H[j].id;if(a.pv[0]>0){var k=c(m);if(k){H[j].width=k.getAttribute("width")?k.getAttribute("width"):"0";H[j].height=k.getAttribute("height")?k.getAttribute("height"):"0";if(O(H[j].swfVersion)){if(a.webkit&&a.webkit<312){U(k)}X(m,true)}else{if(H[j].expressInstall&&!C&&O("6.0.65")&&(a.win||a.mac)){D(H[j])}else{d(k)}}}}else{X(m,true)}}}function U(m){var k=m.getElementsByTagName(P)[0];if(k){var p=Y("embed"),r=k.attributes;if(r){var o=r.length;for(var n=0;n<o;n++){if(r[n].nodeName.toLowerCase()=="data"){p.setAttribute("src",r[n].nodeValue)}else{p.setAttribute(r[n].nodeName,r[n].nodeValue)}}}var q=k.childNodes;if(q){var s=q.length;for(var l=0;l<s;l++){if(q[l].nodeType==1&&q[l].nodeName.toLowerCase()=="param"){p.setAttribute(q[l].getAttribute("name"),q[l].getAttribute("value"))}}}m.parentNode.replaceChild(p,m)}}function F(i){if(a.ie&&a.win&&O("8.0.0")){G.attachEvent("onunload",function(){var k=c(i);if(k){for(var j in k){if(typeof k[j]=="function"){k[j]=function(){}}}k.parentNode.removeChild(k)}})}}function D(j){C=true;var o=c(j.id);if(o){if(j.altContentId){var l=c(j.altContentId);if(l){L=l;T=j.altContentId}}else{L=b(o)}if(!(/%$/.test(j.width))&&parseInt(j.width,10)<310){j.width="310"}if(!(/%$/.test(j.height))&&parseInt(j.height,10)<137){j.height="137"}g.title=g.title.slice(0,47)+" - Flash Player Installation";var n=a.ie&&a.win?"ActiveX":"PlugIn",k=g.title,m="MMredirectURL="+G.location+"&MMplayerType="+n+"&MMdoctitle="+k,p=j.id;if(a.ie&&a.win&&o.readyState!=4){var i=Y("div");p+="SWFObjectNew";i.setAttribute("id",p);o.parentNode.insertBefore(i,o);o.style.display="none";G.attachEvent("onload",function(){o.parentNode.removeChild(o)})}R({data:j.expressInstall,id:K,width:j.width,height:j.height},{flashvars:m},p)}}function d(j){if(a.ie&&a.win&&j.readyState!=4){var i=Y("div");j.parentNode.insertBefore(i,j);i.parentNode.replaceChild(b(j),i);j.style.display="none";G.attachEvent("onload",function(){j.parentNode.removeChild(j)})}else{j.parentNode.replaceChild(b(j),j)}}function b(n){var m=Y("div");if(a.win&&a.ie){m.innerHTML=n.innerHTML}else{var k=n.getElementsByTagName(P)[0];if(k){var o=k.childNodes;if(o){var j=o.length;for(var l=0;l<j;l++){if(!(o[l].nodeType==1&&o[l].nodeName.toLowerCase()=="param")&&!(o[l].nodeType==8)){m.appendChild(o[l].cloneNode(true))}}}}}return m}function R(AE,AC,q){var p,t=c(q);if(typeof AE.id==Z){AE.id=q}if(a.ie&&a.win){var AD="";for(var z in AE){if(AE[z]!=Object.prototype[z]){if(z=="data"){AC.movie=AE[z]}else{if(z.toLowerCase()=="styleclass"){AD+=' class="'+AE[z]+'"'}else{if(z!="classid"){AD+=" "+z+'="'+AE[z]+'"'}}}}}var AB="";for(var y in AC){if(AC[y]!=Object.prototype[y]){AB+='<param name="'+y+'" value="'+AC[y]+'" />'}}t.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+AD+">"+AB+"</object>";F(AE.id);p=c(AE.id)}else{if(a.webkit&&a.webkit<312){var AA=Y("embed");AA.setAttribute("type",W);for(var x in AE){if(AE[x]!=Object.prototype[x]){if(x=="data"){AA.setAttribute("src",AE[x])}else{if(x.toLowerCase()=="styleclass"){AA.setAttribute("class",AE[x])}else{if(x!="classid"){AA.setAttribute(x,AE[x])}}}}}for(var w in AC){if(AC[w]!=Object.prototype[w]){if(w!="movie"){AA.setAttribute(w,AC[w])}}}t.parentNode.replaceChild(AA,t);p=AA}else{var s=Y(P);s.setAttribute("type",W);for(var v in AE){if(AE[v]!=Object.prototype[v]){if(v.toLowerCase()=="styleclass"){s.setAttribute("class",AE[v])}else{if(v!="classid"){s.setAttribute(v,AE[v])}}}}for(var u in AC){if(AC[u]!=Object.prototype[u]&&u!="movie"){E(s,u,AC[u])}}t.parentNode.replaceChild(s,t);p=s}}return p}function E(k,i,j){var l=Y("param");l.setAttribute("name",i);l.setAttribute("value",j);k.appendChild(l)}function c(i){return g.getElementById(i)}function Y(i){return g.createElement(i)}function O(k){var j=a.pv,i=k.split(".");i[0]=parseInt(i[0],10);i[1]=parseInt(i[1],10);i[2]=parseInt(i[2],10);return(j[0]>i[0]||(j[0]==i[0]&&j[1]>i[1])||(j[0]==i[0]&&j[1]==i[1]&&j[2]>=i[2]))?true:false}function A(m,j){if(a.ie&&a.mac){return }var l=g.getElementsByTagName("head")[0],k=Y("style");k.setAttribute("type","text/css");k.setAttribute("media","screen");if(!(a.ie&&a.win)&&typeof g.createTextNode!=Z){k.appendChild(g.createTextNode(m+" {"+j+"}"))}l.appendChild(k);if(a.ie&&a.win&&typeof g.styleSheets!=Z&&g.styleSheets.length>0){var i=g.styleSheets[g.styleSheets.length-1];if(typeof i.addRule==P){i.addRule(m,j)}}}function X(k,i){var j=i?"visible":"hidden";if(S){c(k).style.visibility=j}else{A("#"+k,"visibility:"+j)}}return{registerObject:function(l,i,k){if(!a.w3cdom||!l||!i){return }var j={};j.id=l;j.swfVersion=i;j.expressInstall=k?k:false;H[H.length]=j;X(l,false)},getObjectById:function(l){var i=null;if(a.w3cdom&&S){var j=c(l);if(j){var k=j.getElementsByTagName(P)[0];if(!k||(k&&typeof j.SetVariable!=Z)){i=j}else{if(typeof k.SetVariable!=Z){i=k}}}}return i},embedSWF:function(n,u,r,t,j,m,k,p,s){if(!a.w3cdom||!n||!u||!r||!t||!j){return }r+="";t+="";if(O(j)){X(u,false);var q=(typeof s==P)?s:{};q.data=n;q.width=r;q.height=t;var o=(typeof p==P)?p:{};if(typeof k==P){for(var l in k){if(k[l]!=Object.prototype[l]){if(typeof o.flashvars!=Z){o.flashvars+="&"+l+"="+k[l]}else{o.flashvars=l+"="+k[l]}}}}J(function(){R(q,o,u);if(q.id==u){X(u,true)}})}else{if(m&&!C&&O("6.0.65")&&(a.win||a.mac)){X(u,false);J(function(){var i={};i.id=i.altContentId=u;i.width=r;i.height=t;i.expressInstall=m;D(i)})}}},getFlashPlayerVersion:function(){return{major:a.pv[0],minor:a.pv[1],release:a.pv[2]}},hasFlashPlayerVersion:O,createSWF:function(k,j,i){if(a.w3cdom&&S){return R(k,j,i)}else{return undefined}},createCSS:function(j,i){if(a.w3cdom){A(j,i)}},addDomLoadEvent:J,addLoadEvent:M,getQueryParamValue:function(m){var l=g.location.search||g.location.hash;if(m==null){return l}if(l){var k=l.substring(1).split("&");for(var j=0;j<k.length;j++){if(k[j].substring(0,k[j].indexOf("="))==m){return k[j].substring((k[j].indexOf("=")+1))}}}return""},expressInstallCallback:function(){if(C&&L){var i=c(K);if(i){i.parentNode.replaceChild(L,i);if(T){X(T,true);if(a.ie&&a.win){L.style.display="block"}}L=null;T=null;C=false}}}}}();
\ No newline at end of file
Index: www/static/swf/open-flash-chart.swf
===================================================================

Property changes on: www/static/swf/open-flash-chart.swf
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: www/views/gnuplot.php
===================================================================
--- www/views/gnuplot.php	(revision 1025)
+++ www/views/gnuplot.php	(working copy)
@@ -1,71 +0,0 @@
-<?php
-
-// This view uses gnuplot to render PNG image
-//
-// Expected view variables:
-// $script  gnuplot script without `set terminal` declaration
-// $data    gnuplot auxiliary data
-
-log_assert(isset($script) && isset($data));
-
-// FIXME Only fixed size plots are supported at this point.
-// Getting PostScript & `convert` to render an image at a precise
-// pixel resolution seems to be a nightmare.
-$width = 510;
-$height = 200;
-$ratio = 0.36;
-
-// compute gnuplot script
-//  - gnuplot accepts image size as a ratio of hard-coded image size 640x480
-$plot_script = "
-set terminal postscript eps color enhanced 'Arial' 15
-set terminal postscript eps landscape
-set size ratio {$ratio}
-set size 1,1
-";
-$plot_script .= $script;
-
-// store auxiliary data in a temporary file
-// NOTE: Don't worry about /tmp! If it doesn't exists, tempnam
-// finds the right system temporary folder
-$tmpfname = tempnam("/tmp/", "iagnuplot_");
-log_assert($tmpfname);
-
-$ftemp = fopen($tmpfname, "w");
-// NOTE: the 0 (zero) at the beginning indicates octal constant value
-chmod($tmpfname, 0666);
-log_assert($ftemp);
-fwrite($ftemp, $data);
-fclose($ftemp);
-
-// 'bind' plot script to data
-$plot_script = str_replace("%data%", $tmpfname, $plot_script);
-
-// open gnuplot pipe
-$descriptorspec = array(
-    0 => array("pipe", "r"),
-    1 => array("pipe", "w"),
-);
-
-$process = proc_open("gnuplot | convert -rotate 90 -density 72 "
-                     ."-resample 51x50 -crop {$width}x{$height}+0+0 "
-                     ."-gravity South ps:- png:-",
-                     $descriptorspec, $pipes);
-
-log_assert(is_resource($process), "Could not create gnuplot process");
-
-// feed script to pipe
-list($plot_in, $plot_out) = $pipes;
-fwrite($plot_in, $plot_script);
-fclose($plot_in);
-
-// render PNG
-header("Content-type: image/png");
-fpassthru($plot_out);
-fclose($plot_out);
-
-// clean-up
-proc_close($process);
-unlink($tmpfname);
-
-?>
Index: www/views/header.php
===================================================================
--- www/views/header.php	(revision 1025)
+++ www/views/header.php	(working copy)
@@ -64,6 +64,14 @@
     <script type="text/javascript" src="<?= html_escape(url_static('js/sh/shBrushJava.js')) ?>"></script>
     <script type="text/javascript" src="<?= html_escape(url_static('js/sh/shBrushPython.js')) ?>"></script>
     <script type="text/javascript" src="<?= html_escape(url_static('js/sh/shInit.js')) ?>"></script>
+<?php
+    $action = getattr($_GET, 'action');
+    if ($action == 'rating') {
+?>
+    <script type="text/javascript" src="<?= html_escape(url_static('js/swfobject.js')) ?>"></script>
+<?php
+    }
+?>
 
     <?= getattr($view, 'head') ?>
 </head>
Index: www/views/plot_distribution.php
===================================================================
--- www/views/plot_distribution.php	(revision 1025)
+++ www/views/plot_distribution.php	(working copy)
@@ -1,78 +1,103 @@
 <?php
 
-require_once(IA_ROOT_DIR."www/format/format.php");
+require_once(IA_ROOT_DIR."www/php-ofc-library/open-flash-chart.php");
 
-if ($user) {
-    $user_rating = $user['rating_cache'];
-    if ($user_rating) {
-        $user_rating = rating_scale($user_rating);
-    }
+// create the rating chart
+$chart = new open_flash_chart();
+
+$chart->set_bg_colour("#ffffff");
+
+$title = new title("Distributia ratingului");
+$chart->set_title($title);
+
+// adjust axises and labels
+$x = new x_axis();
+$x->set_colour("#aaaaaa");
+$x->set_grid_colour("#ffffff");
+
+$first_element = array_slice($distribution, 0, 1, TRUE);
+foreach ($first_element as $first_bucket => $count) {
+    $p = $first_bucket;
 }
-else {
-    $user_rating = null;
-}
 
-// gnuplot script
-$script = "
-set terminal postscript noenhanced
-set grid
+$labels = array();
+foreach ($distribution as $bucket => $count) {
+    for (; $p < $bucket; ++$p) {
+        $current_rating = rating_scale($p * $bucket_size);
+        $current_label = $current_rating . " - " . ($current_rating + rating_scale($bucket_size) - 1);
+        $labels[] = $current_label;
+    }
 
-set rmargin 2
-set lmargin 5
-set tmargin 15
-set bmargin 2
-set xtics nomirror
-set ytics nomirror
+    $current_rating = rating_scale($bucket * $bucket_size);
+    $current_label = $current_rating . " - " . ($current_rating + rating_scale($bucket_size) - 1);
+    $labels[] = $current_label;
 
-set title \"Distributie rating (".IA_URL.")\" 0,-.5
+    ++$p;
+}
+$x_axis_labels = new x_axis_labels();
+$x_axis_labels->set_vertical();
+$x_axis_labels->set_labels($labels);
+$x->set_labels($x_axis_labels);
 
-set style line 1 lt 1 lw 4 pt 3 ps 0.5
-set style line 2 lt 3 lw 6 pt 7 ps 1.0
-set style line 3 lt 11 lw 6
-set xrange [350:1130]
-set yrange [0:250]
+$chart->set_x_axis($x);
 
-set xtics 100
-set xtic rotate by -20
+$y = new y_axis();
+$y->set_colour("#aaaaaa");
+$y->set_grid_colour("#aaaaaa");
+$y->set_range(0, max($distribution) * 1.15);
+$y->set_steps(50);
+$chart->set_y_axis($y);
 
-";
+// generate the rating chart
+$bar = new bar_filled();
 
-// legend
-$script .= "
-set key right top box 3
-set key width -1.5
-";
-
-// draw user rating as a parametric curve with constant x
-if ($user && $user_rating) {
-    $script .= "
-set parametric
-const={$user_rating}
-set trange [0:250]
-";
+foreach ($first_element as $first_bucket => $count) {
+    $p = $first_bucket;
 }
+foreach ($distribution as $bucket => $count) {
+    for (; $p < $bucket; ++$p) {
+        $bar_value = new bar_value(0);
+        $current_rating = rating_scale($p * $bucket_size);
+        $bar_value->set_tooltip("Rating: " . $current_rating . " - " .
+            ($current_rating + rating_scale($bucket_size) - 1)
+            . "<br>0 concurenti");
+        $bar->append_value($bar_value);
+    }
 
-// plot distribution & median
-$script .= "
-plot \\
-    \"%data%\" using 1:2 title \"Concurenti\" with impulses ls 3, \\
-    \"%data%\" using 1:2 smooth csplines title \"Aproximare\" with lines ls 1";
+    $bar_value = new bar_value($count);
+    $current_rating = rating_scale($bucket * $bucket_size);
+    $bar_value->set_tooltip("Rating: " . $current_rating . " - " .
+        ($current_rating + rating_scale($bucket_size) - 1)
+        . "<br>" . $count . ($count == 1 ? " concurent" : " concurenti"));
 
-// plot user
-if ($user && $user_rating) {
-    $script .= ", \\
-    const,t title \"{$user['username']} ({$user_rating})\" with lines ls 2
-";
+    if (rating_scale($bucket * $bucket_size) < 520) {
+        $bar_value->set_colour("#00a900");
+    } elseif (rating_scale($bucket * $bucket_size) < 600) {
+        $bar_value->set_colour("#ddcc00");
+    } else {
+        $bar_value->set_colour("ee0000");
+    }
+
+    $bar->append_value($bar_value);
+    ++$p;
 }
 
-// plot data
-// #rating bucket #count
-$data = '';
-foreach ($distribution as $bucket => $count) {
-    $scaled = rating_scale($bucket*$bucket_size);
-    $data .= $scaled." ".$count."\n";
+$chart->add_element($bar);
+
+// mark the rating of the current user
+$user_rating = rating_scale(getattr($user, 'rating_cache', 0));
+foreach ($first_element as $first_bucket => $count) {
+    $x_coord = ($user_rating - rating_scale($first_bucket * $bucket_size + $bucket_size / 2.)) / rating_scale($bucket_size);
 }
 
-// render PNG
-include(IA_ROOT_DIR.'www/views/gnuplot.php');
+$shape = new shape("#0000ff");
+$shape->append_value(new shape_point($x_coord - 0.05, 0));
+$shape->append_value(new shape_point($x_coord + 0.05, 0));
+$shape->append_value(new shape_point($x_coord + 0.05, max($distribution) * 1.15));
+$shape->append_value(new shape_point($x_coord - 0.05, max($distribution) * 1.15));
+$chart->add_element($shape);
+
+// output data
+echo $chart->toString();
+
 ?>
Index: www/views/plot_rating.php
===================================================================
--- www/views/plot_rating.php	(revision 1025)
+++ www/views/plot_rating.php	(working copy)
@@ -1,121 +1,104 @@
 <?php
 
-require_once(IA_ROOT_DIR."www/format/format.php");
+require_once(IA_ROOT_DIR."www/php-ofc-library/open-flash-chart.php");
 
-// date range
+// get data ranges
 if (2 <= count($history)) {
     $keys = array_keys($history);
-    $range_start = $history[$keys[0]]['timestamp'];
-    $range_end = $history[$keys[count($history)-1]]['timestamp'];
+
+    $range_start = floor($history[$keys[0]]['timestamp'] / (24 * 3600));
+    $range_start -= (int)date('d', $history[$keys[0]]['timestamp']) - 1;
+    $range_end = floor($history[$keys[count($history) - 1]]['timestamp'] / (24 * 3600));
+
     $rating_start = $history[$keys[0]]['rating'];
     $rating_end = $rating_start;
     foreach ($history as $round_id => $round) {
         $rating_start = min($rating_start, $round['rating']);
         $rating_end = max($rating_end, $round['rating']);
     }
-    $rating_start = rating_scale($rating_start)-50;
-    $rating_end = rating_scale($rating_end)+50;
-}
-else {
+    $rating_start = rating_scale($rating_start) - 50;
+    $rating_start -= $rating_start % 50;
+    $peak_point = rating_scale($rating_end);
+    $rating_end = $peak_point + 50;
+} else {
     // 2004-01-01
-    $range_start = mktime(1, 0, 0, 1, 1, 2004);
-    $range_end = time();
-    $rating_start = 0; $rating_end = 1000;
+    $range_start = floor(mktime(1, 0, 0, 1, 1, 2004) / (24 * 3600));
+    $range_end = floor(time() / (24 * 3600));
+    $rating_start = 0;
+    $rating_end = $peak_point = 1000;
 }
-// compute months between date range to show as xtics
-list($dy, $dm, $dd) = split('-', date('Y-m-d', $range_end));
-$i = 0;
-$xtics = array();
-$scale = ceil(($range_end-$range_start)/(30*24*3600)/12);
-while (true) {
-    $dx = mktime(1, 0, 0, $dm - $i, $dd, $dy); 
-    $xtics[] = date('Y-m-d', $dx);
-    if ($dx < $range_start) {
-        break;
-    }
-    $i += $scale;
-}
-$xtics = array_reverse($xtics);
 
-// format date ranges for gnuplot
-$range_start = date('Y-m-d', $range_start - 15*24*3600);
-$range_end = date('Y-m-d', $range_end + 15*24*3600);
+// create the rating chart
+$chart = new open_flash_chart();
 
-// gnuplot script
-$script = "
-set terminal postscript noenhanced
-set locale \"ro_RO.UTF-8\"
-set xdata time
-set timefmt \"%Y-%m-%d\"
-set format x \"%b %y\"
-set grid
+$chart->set_bg_colour("#ffffff");
 
-set rmargin 5
-set lmargin 5
-set tmargin 12
-set bmargin 2
-set xtics nomirror
-set ytics nomirror
+$title = new title("Evolutia ratingului pentru " . $user['username']);
+$chart->set_title($title);
 
-set title \"Evolutie rating pentru {$user['username']} (".IA_URL.")\" 0,-.5
+// adjust axises and labels
+$x = new x_axis();
+$x->set_colour("#aaaaaa");
+$x->set_grid_colour("#aaaaaa");
+$x->set_range($range_start, $range_end);
+$x->set_steps(122);
 
-set style line 1 lt 1 lw 4 pt 3 ps 0.5
-set style line 2 lt 3 lw 3 pt 7 ps 1.0
-set style line 3 lt 11 lw 3
-set xrange [\"{$range_start}\":\"{$range_end}\"]
-set yrange [{$rating_start}:{$rating_end}]
+$labels = array();
+for ($p = $range_start; $p <= $range_end; ++$p) {
+    $labels[] = date("d-m-Y", $p * 24 * 3600);
+}
 
-set xtics ('".join("', '", $xtics)."')
-set xtic rotate by -20
+$x_axis_labels = new x_axis_labels();
+$x_axis_labels->set_labels($labels);
+$x_axis_labels->set_vertical();
+$x_axis_labels->set_steps(122);
 
-set clip
-";
+$x->set_labels($x_axis_labels);
+$chart->set_x_axis($x);
 
-// display round_id labels
-$i = 1;
-$scale = ceil(($rating_end-$rating_start)/20);
+$y = new y_axis();
+$y->set_colour("#aaaaaa");
+$y->set_grid_colour("#aaaaaa");
+$y->set_range($rating_start, $rating_end);
+$y->set_steps(50);
+$chart->set_y_axis($y);
+
+// generate the rating chart
+$data = array();
+
+$p = $range_start;
 foreach ($history as $round_id => $round) {
-    $date = date("Y-m-d", $round['timestamp']);
-    $rating = rating_scale($round['rating']) + ($i % 2 ? $scale : -$scale);
-    $align = "right";
-    $script .= "set label {$i} \"{$i}\" at \"{$date}\",{$rating} {$align} font \"Helvetica,19\" back tc lt 9\n";
-    $i++;
-}
+    $current_x = floor($round['timestamp'] / (24 * 3600));
+    for (; $p < $current_x; ++$p) {
+        $data[] = null;
+    }
 
-// legend
-$script .= "
-set key right bottom box 3
-set key width -1.5
-";
+    $current_y = rating_scale($round['rating']);
+    if ($current_y < 520) {
+        $colour = "#00a900";
+    } elseif ($current_y < 600) {
+        $colour = "#ddcc00";
+    } else {
+        $colour = "#ee0000";
+    }
+    $dot = new dot_value($current_y, $colour);
+    if ($current_y == $peak_point) {
+        $dot->set_tooltip("Max: #val#<br>" . $round['round_title']);
+    } else {
+        $dot->set_tooltip("#val#<br>" . $round['round_title']);
+    }
 
-if (1 <= count($history)) {
-    // plot ratings, deviations & median
-    $script .= "
-plot \\
-    \"%data%\" using 1:2 title \"Rating\" with lines ls 1, \\
-    \"%data%\" using 1:2 notitle with points ls 2
-";
+    $data[] = $dot;
+    ++$p;
 }
-else {
-    // when nothing to plot, use a bogus (non-visible) function plot so that
-    // gnuplot doesn't fail
-    $script .= "
-set label \"(date insuficiente pentru a desena graficul)\" at graph 0.5,0.5 center
-plot 0 notitle with lines ls 1
-";
-}
 
-// plot data
-// #date #rating #deviation
-$data = '';
-foreach ($history as $round_id => $round) {
-    $timestamp = (int)$round['timestamp'];
-    $data .= date("Y-m-d", $timestamp)
-             ." ".rating_scale($round['rating'])." "
-             .rating_scale($round['deviation'])."\n";
-}
+$line_dot = new line_dot();
+$line_dot->set_dot_size(4);
+$line_dot->set_halo_size(1);
+$line_dot->set_values($data);
+$chart->add_element($line_dot);
 
-// render PNG
-include(IA_ROOT_DIR.'www/views/gnuplot.php');
+// output data
+echo $chart->toString();
 
 ?>
