[Ovirt-devel] REVISED: [PATCH server] Add a rudimentary flash chart written in flex framework to summary pages.

Steve Linabery slinabery at redhat.com
Fri Oct 10 15:33:40 UTC 2008


Hi,

Here's a resubmission of this patch, with git pre-commit hooks enabled this time.

Thank you for testing!

Steve
-------------- next part --------------
>From 2f4c9126262cb9cc2272eb6ca62436c0e2746ab9 Mon Sep 17 00:00:00 2001
From: Steve Linabery <slinabery at redhat.com>
Date: Fri, 10 Oct 2008 10:29:53 -0500
Subject: [PATCH server] Add to summary pages a rudimentary flash chart written in flex framework

---
 src/app/controllers/graph_controller.rb            |   30 +-
 src/app/views/graph/flexchart_data.rhtml           |    1 +
 src/app/views/graph/history_graphs.rhtml           |   87 +---
 src/config/routes.rb                               |    1 +
 src/flexchart/README.txt                           |    8 +
 src/flexchart/com/adobe/serialization/json/JSON.as |   85 +++
 .../com/adobe/serialization/json/JSONDecoder.as    |  221 ++++++++
 .../com/adobe/serialization/json/JSONEncoder.as    |  299 +++++++++++
 .../com/adobe/serialization/json/JSONParseError.as |   87 +++
 .../com/adobe/serialization/json/JSONToken.as      |  104 ++++
 .../com/adobe/serialization/json/JSONTokenType.as  |   67 +++
 .../com/adobe/serialization/json/JSONTokenizer.as  |  547 ++++++++++++++++++++
 src/flexchart/flexchart.mxml                       |   20 +
 src/flexchart/org/ovirt/ChartLoader.as             |   64 +++
 src/flexchart/org/ovirt/DataSeries.as              |   42 ++
 src/flexchart/org/ovirt/DataSource.as              |   57 ++
 src/public/javascripts/jquery.flash.js             |  288 ++++++++++
 17 files changed, 1930 insertions(+), 78 deletions(-)
 create mode 100644 src/app/views/graph/flexchart_data.rhtml
 create mode 100644 src/flexchart/README.txt
 create mode 100644 src/flexchart/com/adobe/serialization/json/JSON.as
 create mode 100644 src/flexchart/com/adobe/serialization/json/JSONDecoder.as
 create mode 100644 src/flexchart/com/adobe/serialization/json/JSONEncoder.as
 create mode 100644 src/flexchart/com/adobe/serialization/json/JSONParseError.as
 create mode 100644 src/flexchart/com/adobe/serialization/json/JSONToken.as
 create mode 100644 src/flexchart/com/adobe/serialization/json/JSONTokenType.as
 create mode 100644 src/flexchart/com/adobe/serialization/json/JSONTokenizer.as
 create mode 100644 src/flexchart/flexchart.mxml
 create mode 100644 src/flexchart/org/ovirt/ChartLoader.as
 create mode 100644 src/flexchart/org/ovirt/DataSeries.as
 create mode 100644 src/flexchart/org/ovirt/DataSource.as
 create mode 100644 src/public/javascripts/jquery.flash.js

diff --git a/src/app/controllers/graph_controller.rb b/src/app/controllers/graph_controller.rb
index dbe2afc..6450935 100644
--- a/src/app/controllers/graph_controller.rb
+++ b/src/app/controllers/graph_controller.rb
@@ -3,7 +3,24 @@ require 'util/stats/Stats'
 class GraphController < ApplicationController
   layout nil
 
-  # generate layout for avaialability bar graphs
+  def flexchart_data
+
+    #FIXME: use the stats package aggregation (when it's available)
+    #instead of the old method
+    graph_obj = history_graph_data_object
+
+    #FIXME: for this release, the flexchart shows only peak values,
+    #       and only shows a default of the last 40 data points in rrd.
+    graph_data = { :labels => graph_obj[:timepoints].last(40),
+                   :values => graph_obj[:dataset][2][:values].last(40) }
+    my_data = graph_data[:labels].zip(graph_data[:values])
+    @graph = { :vectors => my_data,
+               :max_value => graph_obj[:total_peak]
+             }
+  end
+
+
+  # generate layout for availability bar graphs
   def availability_graph
     @id = params[:id]
     @target = params[:target]
@@ -67,6 +84,10 @@ class GraphController < ApplicationController
 
   # retrieves data for history graphs
   def history_graph_data
+    render :json => history_graph_data_object
+  end
+
+  def history_graph_data_object
     history_graphs
     myDays = params[:days]
     target = params[:target]
@@ -212,9 +233,10 @@ class GraphController < ApplicationController
                 :stroke => @avg_history[:color],
                 :strokeWidth => 1
             }
-       ]
+       ],
+       :total_peak => total_peak
     }
-    render :json => graph_object
+
   end
 
 
@@ -261,7 +283,7 @@ class GraphController < ApplicationController
             }
        ]
     }
-    render :json => graph_object
+
 
   end
   
diff --git a/src/app/views/graph/flexchart_data.rhtml b/src/app/views/graph/flexchart_data.rhtml
new file mode 100644
index 0000000..a79ce06
--- /dev/null
+++ b/src/app/views/graph/flexchart_data.rhtml
@@ -0,0 +1 @@
+<%= @graph.to_json %>
diff --git a/src/app/views/graph/history_graphs.rhtml b/src/app/views/graph/history_graphs.rhtml
index 2b6874f..f372e4b 100644
--- a/src/app/views/graph/history_graphs.rhtml
+++ b/src/app/views/graph/history_graphs.rhtml
@@ -1,76 +1,15 @@
+<%= javascript_include_tag "jquery.flash.js" %>
+<div id="the-div-name"></div>
 <script type="text/javascript">
-
-var graph = "load_history";
-var days  = "7";
-
-function swap_history_graph(newgraph, newdays){
-    if(newgraph == null) newgraph = graph
-    if(newdays == null) newdays = days
-    $('.history_graph').hide(); 
-    $('#' + newgraph + "_" + newdays).parent().show();
-    eval("draw_" + newgraph + "_" + newdays + "_graph_get_data()");
-}
-function swap_history_graph_target(title, newgraph){
-    swap_history_graph(newgraph, null);
-    $('#history_graph_selection').html(title + '  <%= image_tag 'icon_menu_arrow.gif' %>');
-    graph = newgraph
-}
-function swap_history_graph_time(title, newdays){
-    swap_history_graph(null, newdays);
-    $('#history_graph_time_selection').html(title + '  <%= image_tag 'icon_menu_arrow.gif' %>');
-    days = newdays
-}
-
-</script>
-
-<%= render :partial => '/layouts/graph', :locals => { :drawMe => false,  :includeDiv => false, :methodName=> 'draw_cpu_history_1_graph',     :div_id => 'cpu_history_1',     :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 20,  :scaleX => 173,   :ticksY => 10, :scaleY => 110,  :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'cpu',    :poolType => @poolType, :days => 1   } ) } %>
-<%= render :partial => '/layouts/graph', :locals => { :drawMe => false,  :includeDiv => false, :methodName=> 'draw_cpu_history_7_graph',     :div_id => 'cpu_history_7',     :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 39, :scaleX => 272,  :ticksY => 10, :scaleY => 110,  :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'cpu',    :poolType => @poolType, :days => 7   } ) } %>
-<%= render :partial => '/layouts/graph', :locals => { :drawMe => false,  :includeDiv => false, :methodName=> 'draw_cpu_history_30_graph',    :div_id => 'cpu_history_30',    :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 120,  :scaleX => 1200,  :ticksY => 10, :scaleY => 110,  :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'cpu',    :poolType => @poolType, :days => 30  } ) } %>
-<%= render :partial => '/layouts/graph', :locals => { :drawMe => false,  :includeDiv => false, :methodName=> 'draw_memory_history_1_graph',  :div_id => 'memory_history_1',  :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 20,  :scaleX => 173,   :ticksY => 50, :scaleY => 756,  :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'memory', :poolType => @poolType, :days => 1   } ) } %>
-<%= render :partial => '/layouts/graph', :locals => { :drawMe => false,  :includeDiv => false, :methodName=> 'draw_memory_history_7_graph',  :div_id => 'memory_history_7',  :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 39, :scaleX => 272,  :ticksY => 50, :scaleY => 756,  :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'memory', :poolType => @poolType, :days => 7   } ) } %>
-<%= render :partial => '/layouts/graph', :locals => { :drawMe => false,  :includeDiv => false, :methodName=> 'draw_memory_history_30_graph', :div_id => 'memory_history_30', :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 120,  :scaleX => 1162,  :ticksY => 50, :scaleY => 756,  :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'memory', :poolType => @poolType, :days => 30  } ) } %>
-<%= render :partial => '/layouts/graph', :locals => { :drawMe => false,  :includeDiv => false, :methodName=> 'draw_load_history_1_graph',    :div_id => 'load_history_1',    :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 20,  :scaleX => 173,   :ticksY => 2,  :scaleY => 23,   :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'load',   :poolType => @poolType, :days => 1   } ) } %>
-<%= render :partial => '/layouts/graph', :locals => { :drawMe => false,  :includeDiv => false, :methodName=> 'draw_load_history_7_graph',    :div_id => 'load_history_7',    :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 39, :scaleX => 272,  :ticksY => 2,  :scaleY => 23,   :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'load',   :poolType => @poolType, :days => 7   } ) } %>
-<%= render :partial => '/layouts/graph', :locals => { :drawMe => false,  :includeDiv => false, :methodName=> 'draw_load_history_30_graph',   :div_id => 'load_history_30',   :chartType => 'line', :yGridLines => 'lightgrey', :xGridLines => 'lightgrey', :ticksX => 120,  :scaleX => 1200,  :ticksY => 2,  :scaleY => 23,   :url => (url_for :escape => false, :controller => 'graph', :action => 'history_graph_data', :id => @id, :params => { :target => 'load',   :poolType => @poolType, :days => 30  } ) } %>
-
-<div id="history_graphs">
-  <div id="history_graphs_control">
-   <div class="history_graphs_menu">
-      <ul>
-        <li><div id="history_graph_selection" class="history_graph_menu_header">Overall Load   <%= image_tag 'icon_menu_arrow.gif' %></div></li>
-        <li class="history_graph_menu_item history_graph_menu_fitem"><a href="#" onclick="swap_history_graph_target('Overall Load', 'load_history')" >Overall Load</a></li>
-        <li class="history_graph_menu_item"><a href="#" onclick="swap_history_graph_target('CPU History', 'cpu_history');" >CPU History</a></li>
-        <li class="history_graph_menu_item history_graph_menu_litem"><a href="#" onclick="swap_history_graph_target('Memory History', 'memory_history');">Memory History</a></li>
-      </ul>
-   </div>
-   <div class="history_graphs_menu">
-      <ul>
-        <li><div id="history_graph_time_selection" class="history_graph_menu_header">Last 7 Days   <%= image_tag 'icon_menu_arrow.gif' %></div></li>
-        <li class="history_graph_menu_item history_graph_menu_fitem"<a href="#" onclick="swap_history_graph_time('Last 24 Hours', '1')" >Last 24 Hours</a></li>
-        <li class="history_graph_menu_item"><a href="#" onclick="swap_history_graph_time('Last 7 days', '7')" >Last 7  Days</a></li>
-        <li class="history_graph_menu_item history_graph_menu_litem"><a href="#" onclick="swap_history_graph_time('Last 30 Days', '30')" >Last 30 Days</a></li>
-      </ul>
-   </div>
-   <div class="history_graphs_legend">
-       <font color="<%= @peak_history[:color]   %>">Peak    </font>
-       <font color="<%= @avg_history[:color] %>">Average    </font>
-       <font color="<%= @roll_peak_history[:color]   %>">Rolling Peak    </font>
-       <font color="<%= @roll_avg_history[:color] %>">Rolling Average    </font>
-   </div>
-  </div>
-  <div id="history_graphs_graphs">
-    <div class="history_graph"><div id="cpu_history_1">       </div></div>
-    <div class="history_graph"><div id="cpu_history_7">       </div></div>
-    <div class="history_graph"><div id="cpu_history_30">      </div></div>
-    <div class="history_graph"><div id="memory_history_1">    </div></div>
-    <div class="history_graph"><div id="memory_history_7">    </div></div>
-    <div class="history_graph"><div id="memory_history_30">   </div></div>
-    <div class="history_graph"><div id="load_history_1">      </div></div>
-    <div class="history_graph"><div id="load_history_7">      </div></div>
-    <div class="history_graph"><div id="load_history_30">     </div></div>
-  </div>
-</div>
-
-<script type="text/javascript">
-    swap_history_graph(null, null); // display 1st graph
+$(document).ready(function(){
+$('#the-div-name').flash(
+        {
+          src: '/ovirt/flexchart.swf',
+          width: 720,
+          height: 300,
+          flashvars: { flexchart_data: '/ovirt/graph/flexchart_data/<%= @id %>/memory/1' }
+        },
+        { version: 9 }
+    );
+});
 </script>
diff --git a/src/config/routes.rb b/src/config/routes.rb
index 6f8e481..8d538cb 100644
--- a/src/config/routes.rb
+++ b/src/config/routes.rb
@@ -41,6 +41,7 @@ ActionController::Routing::Routes.draw do |map|
   map.connect ':controller/service.wsdl', :action => 'wsdl'
 
   # Install the default route as the lowest priority.
+  map.connect 'graph/flexchart_data/:id/:target/:days', :controller => 'graph', :action => 'flexchart_data'
   map.connect ':controller/:action/:id.:format'
   map.connect ':controller/:action/:id'
 
diff --git a/src/flexchart/README.txt b/src/flexchart/README.txt
new file mode 100644
index 0000000..66eb183
--- /dev/null
+++ b/src/flexchart/README.txt
@@ -0,0 +1,8 @@
+Until mxmlc gets packaged and this becomes part of autobuild,
+you must obtain the open flex SDK to build the swf movie.
+
+Once you have mxmlc on your system, run:
+
+mxmlc flexchart.mxml
+
+in this directory, and copy the resulting file flexchart.swf to /usr/share/ovirt-server/public on your appliance.
diff --git a/src/flexchart/com/adobe/serialization/json/JSON.as b/src/flexchart/com/adobe/serialization/json/JSON.as
new file mode 100644
index 0000000..8cd9286
--- /dev/null
+++ b/src/flexchart/com/adobe/serialization/json/JSON.as
@@ -0,0 +1,85 @@
+/*
+  Copyright (c) 2008, Adobe Systems Incorporated
+  All rights reserved.
+
+  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.
+
+  * Neither the name of Adobe Systems Incorporated nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR
+  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.
+*/
+
+package com.adobe.serialization.json {
+
+	/**
+	 * This class provides encoding and decoding of the JSON format.
+	 *
+	 * Example usage:
+	 * <code>
+	 * 		// create a JSON string from an internal object
+	 * 		JSON.encode( myObject );
+	 *
+	 *		// read a JSON string into an internal object
+	 *		var myObject:Object = JSON.decode( jsonString );
+	 *	</code>
+	 */
+	public class JSON {
+
+
+		/**
+		 * Encodes a object into a JSON string.
+		 *
+		 * @param o The object to create a JSON string for
+		 * @return the JSON string representing o
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public static function encode( o:Object ):String {
+
+			var encoder:JSONEncoder = new JSONEncoder( o );
+			return encoder.getString();
+
+		}
+
+		/**
+		 * Decodes a JSON string into a native object.
+		 *
+		 * @param s The JSON string representing the object
+		 * @return A native object as specified by s
+		 * @throw JSONParseError
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public static function decode( s:String ):* {
+
+			var decoder:JSONDecoder = new JSONDecoder( s )
+			return decoder.getValue();
+
+		}
+
+	}
+
+}
\ No newline at end of file
diff --git a/src/flexchart/com/adobe/serialization/json/JSONDecoder.as b/src/flexchart/com/adobe/serialization/json/JSONDecoder.as
new file mode 100644
index 0000000..83bba1c
--- /dev/null
+++ b/src/flexchart/com/adobe/serialization/json/JSONDecoder.as
@@ -0,0 +1,221 @@
+/*
+  Copyright (c) 2008, Adobe Systems Incorporated
+  All rights reserved.
+
+  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.
+
+  * Neither the name of Adobe Systems Incorporated nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR
+  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.
+*/
+
+package com.adobe.serialization.json {
+
+	public class JSONDecoder {
+
+		/** The value that will get parsed from the JSON string */
+		private var value:*;
+
+		/** The tokenizer designated to read the JSON string */
+		private var tokenizer:JSONTokenizer;
+
+		/** The current token from the tokenizer */
+		private var token:JSONToken;
+
+		/**
+		 * Constructs a new JSONDecoder to parse a JSON string
+		 * into a native object.
+		 *
+		 * @param s The JSON string to be converted
+		 *		into a native object
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public function JSONDecoder( s:String ) {
+
+			tokenizer = new JSONTokenizer( s );
+
+			nextToken();
+			value = parseValue();
+		}
+
+		/**
+		 * Gets the internal object that was created by parsing
+		 * the JSON string passed to the constructor.
+		 *
+		 * @return The internal object representation of the JSON
+		 * 		string that was passed to the constructor
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public function getValue():* {
+			return value;
+		}
+
+		/**
+		 * Returns the next token from the tokenzier reading
+		 * the JSON string
+		 */
+		private function nextToken():JSONToken {
+			return token = tokenizer.getNextToken();
+		}
+
+		/**
+		 * Attempt to parse an array
+		 */
+		private function parseArray():Array {
+			// create an array internally that we're going to attempt
+			// to parse from the tokenizer
+			var a:Array = new Array();
+
+			// grab the next token from the tokenizer to move
+			// past the opening [
+			nextToken();
+
+			// check to see if we have an empty array
+			if ( token.type == JSONTokenType.RIGHT_BRACKET ) {
+				// we're done reading the array, so return it
+				return a;
+			}
+
+			// deal with elements of the array, and use an "infinite"
+			// loop because we could have any amount of elements
+			while ( true ) {
+				// read in the value and add it to the array
+				a.push ( parseValue() );
+
+				// after the value there should be a ] or a ,
+				nextToken();
+
+				if ( token.type == JSONTokenType.RIGHT_BRACKET ) {
+					// we're done reading the array, so return it
+					return a;
+				} else if ( token.type == JSONTokenType.COMMA ) {
+					// move past the comma and read another value
+					nextToken();
+				} else {
+					tokenizer.parseError( "Expecting ] or , but found " + token.value );
+				}
+			}
+            return null;
+		}
+
+		/**
+		 * Attempt to parse an object
+		 */
+		private function parseObject():Object {
+			// create the object internally that we're going to
+			// attempt to parse from the tokenizer
+			var o:Object = new Object();
+
+			// store the string part of an object member so
+			// that we can assign it a value in the object
+			var key:String
+
+			// grab the next token from the tokenizer
+			nextToken();
+
+			// check to see if we have an empty object
+			if ( token.type == JSONTokenType.RIGHT_BRACE ) {
+				// we're done reading the object, so return it
+				return o;
+			}
+
+			// deal with members of the object, and use an "infinite"
+			// loop because we could have any amount of members
+			while ( true ) {
+
+				if ( token.type == JSONTokenType.STRING ) {
+					// the string value we read is the key for the object
+					key = String( token.value );
+
+					// move past the string to see what's next
+					nextToken();
+
+					// after the string there should be a :
+					if ( token.type == JSONTokenType.COLON ) {
+
+						// move past the : and read/assign a value for the key
+						nextToken();
+						o[key] = parseValue();
+
+						// move past the value to see what's next
+						nextToken();
+
+						// after the value there's either a } or a ,
+						if ( token.type == JSONTokenType.RIGHT_BRACE ) {
+							// // we're done reading the object, so return it
+							return o;
+
+						} else if ( token.type == JSONTokenType.COMMA ) {
+							// skip past the comma and read another member
+							nextToken();
+						} else {
+							tokenizer.parseError( "Expecting } or , but found " + token.value );
+						}
+					} else {
+						tokenizer.parseError( "Expecting : but found " + token.value );
+					}
+				} else {
+					tokenizer.parseError( "Expecting string but found " + token.value );
+				}
+			}
+            return null;
+		}
+
+		/**
+		 * Attempt to parse a value
+		 */
+		private function parseValue():Object
+		{
+			// Catch errors when the input stream ends abruptly
+			if ( token == null )
+			{
+				tokenizer.parseError( "Unexpected end of input" );
+			}
+
+			switch ( token.type ) {
+				case JSONTokenType.LEFT_BRACE:
+					return parseObject();
+
+				case JSONTokenType.LEFT_BRACKET:
+					return parseArray();
+
+				case JSONTokenType.STRING:
+				case JSONTokenType.NUMBER:
+				case JSONTokenType.TRUE:
+				case JSONTokenType.FALSE:
+				case JSONTokenType.NULL:
+					return token.value;
+
+				default:
+					tokenizer.parseError( "Unexpected " + token.value );
+
+			}
+            return null;
+		}
+	}
+}
diff --git a/src/flexchart/com/adobe/serialization/json/JSONEncoder.as b/src/flexchart/com/adobe/serialization/json/JSONEncoder.as
new file mode 100644
index 0000000..8297b4f
--- /dev/null
+++ b/src/flexchart/com/adobe/serialization/json/JSONEncoder.as
@@ -0,0 +1,299 @@
+/*
+  Copyright (c) 2008, Adobe Systems Incorporated
+  All rights reserved.
+
+  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.
+
+  * Neither the name of Adobe Systems Incorporated nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR
+  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.
+*/
+
+package com.adobe.serialization.json
+{
+
+	import flash.utils.describeType;
+
+	public class JSONEncoder {
+
+		/** The string that is going to represent the object we're encoding */
+		private var jsonString:String;
+
+		/**
+		 * Creates a new JSONEncoder.
+		 *
+		 * @param o The object to encode as a JSON string
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public function JSONEncoder( value:* ) {
+			jsonString = convertToString( value );
+
+		}
+
+		/**
+		 * Gets the JSON string from the encoder.
+		 *
+		 * @return The JSON string representation of the object
+		 * 		that was passed to the constructor
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public function getString():String {
+			return jsonString;
+		}
+
+		/**
+		 * Converts a value to it's JSON string equivalent.
+		 *
+		 * @param value The value to convert.  Could be any
+		 *		type (object, number, array, etc)
+		 */
+		private function convertToString( value:* ):String {
+
+			// determine what value is and convert it based on it's type
+			if ( value is String ) {
+
+				// escape the string so it's formatted correctly
+				return escapeString( value as String );
+
+			} else if ( value is Number ) {
+
+				// only encode numbers that finate
+				return isFinite( value as Number) ? value.toString() : "null";
+
+			} else if ( value is Boolean ) {
+
+				// convert boolean to string easily
+				return value ? "true" : "false";
+
+			} else if ( value is Array ) {
+
+				// call the helper method to convert an array
+				return arrayToString( value as Array );
+
+			} else if ( value is Object && value != null ) {
+
+				// call the helper method to convert an object
+				return objectToString( value );
+			}
+            return "null";
+		}
+
+		/**
+		 * Escapes a string accoding to the JSON specification.
+		 *
+		 * @param str The string to be escaped
+		 * @return The string with escaped special characters
+		 * 		according to the JSON specification
+		 */
+		private function escapeString( str:String ):String {
+			// create a string to store the string's jsonstring value
+			var s:String = "";
+			// current character in the string we're processing
+			var ch:String;
+			// store the length in a local variable to reduce lookups
+			var len:Number = str.length;
+
+			// loop over all of the characters in the string
+			for ( var i:int = 0; i < len; i++ ) {
+
+				// examine the character to determine if we have to escape it
+				ch = str.charAt( i );
+				switch ( ch ) {
+
+					case '"':	// quotation mark
+						s += "\\\"";
+						break;
+
+					//case '/':	// solidus
+					//	s += "\\/";
+					//	break;
+
+					case '\\':	// reverse solidus
+						s += "\\\\";
+						break;
+
+					case '\b':	// bell
+						s += "\\b";
+						break;
+
+					case '\f':	// form feed
+						s += "\\f";
+						break;
+
+					case '\n':	// newline
+						s += "\\n";
+						break;
+
+					case '\r':	// carriage return
+						s += "\\r";
+						break;
+
+					case '\t':	// horizontal tab
+						s += "\\t";
+						break;
+
+					default:	// everything else
+
+						// check for a control character and escape as unicode
+						if ( ch < ' ' ) {
+							// get the hex digit(s) of the character (either 1 or 2 digits)
+							var hexCode:String = ch.charCodeAt( 0 ).toString( 16 );
+
+							// ensure that there are 4 digits by adjusting
+							// the # of zeros accordingly.
+							var zeroPad:String = hexCode.length == 2 ? "00" : "000";
+
+							// create the unicode escape sequence with 4 hex digits
+							s += "\\u" + zeroPad + hexCode;
+						} else {
+
+							// no need to do any special encoding, just pass-through
+							s += ch;
+
+						}
+				}	// end switch
+
+			}	// end for loop
+
+			return "\"" + s + "\"";
+		}
+
+		/**
+		 * Converts an array to it's JSON string equivalent
+		 *
+		 * @param a The array to convert
+		 * @return The JSON string representation of <code>a</code>
+		 */
+		private function arrayToString( a:Array ):String {
+			// create a string to store the array's jsonstring value
+			var s:String = "";
+
+			// loop over the elements in the array and add their converted
+			// values to the string
+			for ( var i:int = 0; i < a.length; i++ ) {
+				// when the length is 0 we're adding the first element so
+				// no comma is necessary
+				if ( s.length > 0 ) {
+					// we've already added an element, so add the comma separator
+					s += ","
+				}
+
+				// convert the value to a string
+				s += convertToString( a[i] );
+			}
+
+			// KNOWN ISSUE:  In ActionScript, Arrays can also be associative
+			// objects and you can put anything in them, ie:
+			//		myArray["foo"] = "bar";
+			//
+			// These properties aren't picked up in the for loop above because
+			// the properties don't correspond to indexes.  However, we're
+			// sort of out luck because the JSON specification doesn't allow
+			// these types of array properties.
+			//
+			// So, if the array was also used as an associative object, there
+			// may be some values in the array that don't get properly encoded.
+			//
+			// A possible solution is to instead encode the Array as an Object
+			// but then it won't get decoded correctly (and won't be an
+			// Array instance)
+
+			// close the array and return it's string value
+			return "[" + s + "]";
+		}
+
+		/**
+		 * Converts an object to it's JSON string equivalent
+		 *
+		 * @param o The object to convert
+		 * @return The JSON string representation of <code>o</code>
+		 */
+		private function objectToString( o:Object ):String
+		{
+			// create a string to store the object's jsonstring value
+			var s:String = "";
+
+			// determine if o is a class instance or a plain object
+			var classInfo:XML = describeType( o );
+			if ( classInfo. at name.toString() == "Object" )
+			{
+				// the value of o[key] in the loop below - store this
+				// as a variable so we don't have to keep looking up o[key]
+				// when testing for valid values to convert
+				var value:Object;
+
+				// loop over the keys in the object and add their converted
+				// values to the string
+				for ( var key:String in o )
+				{
+					// assign value to a variable for quick lookup
+					value = o[key];
+
+					// don't add function's to the JSON string
+					if ( value is Function )
+					{
+						// skip this key and try another
+						continue;
+					}
+
+					// when the length is 0 we're adding the first item so
+					// no comma is necessary
+					if ( s.length > 0 ) {
+						// we've already added an item, so add the comma separator
+						s += ","
+					}
+
+					s += escapeString( key ) + ":" + convertToString( value );
+				}
+			}
+			else // o is a class instance
+			{
+				// Loop over all of the variables and accessors in the class and
+				// serialize them along with their values.
+				for each ( var v:XML in classInfo..*.( name() == "variable" || name() == "accessor" ) )
+				{
+					// When the length is 0 we're adding the first item so
+					// no comma is necessary
+					if ( s.length > 0 ) {
+						// We've already added an item, so add the comma separator
+						s += ","
+					}
+
+					s += escapeString( v. at name.toString() ) + ":"
+							+ convertToString( o[ v. at name ] );
+				}
+
+			}
+
+			return "{" + s + "}";
+		}
+
+
+	}
+
+}
diff --git a/src/flexchart/com/adobe/serialization/json/JSONParseError.as b/src/flexchart/com/adobe/serialization/json/JSONParseError.as
new file mode 100644
index 0000000..4040910
--- /dev/null
+++ b/src/flexchart/com/adobe/serialization/json/JSONParseError.as
@@ -0,0 +1,87 @@
+/*
+  Copyright (c) 2008, Adobe Systems Incorporated
+  All rights reserved.
+
+  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.
+
+  * Neither the name of Adobe Systems Incorporated nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR
+  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.
+*/
+
+package com.adobe.serialization.json {
+
+	/**
+	 *
+	 *
+	 */
+	public class JSONParseError extends Error 	{
+
+		/** The location in the string where the error occurred */
+		private var _location:int;
+
+		/** The string in which the parse error occurred */
+		private var _text:String;
+
+		/**
+		 * Constructs a new JSONParseError.
+		 *
+		 * @param message The error message that occured during parsing
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public function JSONParseError( message:String = "", location:int = 0, text:String = "") {
+			super( message );
+			name = "JSONParseError";
+			_location = location;
+			_text = text;
+		}
+
+		/**
+		 * Provides read-only access to the location variable.
+		 *
+		 * @return The location in the string where the error occurred
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public function get location():int {
+			return _location;
+		}
+
+		/**
+		 * Provides read-only access to the text variable.
+		 *
+		 * @return The string in which the error occurred
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public function get text():String {
+			return _text;
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/src/flexchart/com/adobe/serialization/json/JSONToken.as b/src/flexchart/com/adobe/serialization/json/JSONToken.as
new file mode 100644
index 0000000..0296f13
--- /dev/null
+++ b/src/flexchart/com/adobe/serialization/json/JSONToken.as
@@ -0,0 +1,104 @@
+/*
+  Copyright (c) 2008, Adobe Systems Incorporated
+  All rights reserved.
+
+  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.
+
+  * Neither the name of Adobe Systems Incorporated nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR
+  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.
+*/
+
+package com.adobe.serialization.json {
+
+	public class JSONToken {
+
+		private var _type:int;
+		private var _value:Object;
+
+		/**
+		 * Creates a new JSONToken with a specific token type and value.
+		 *
+		 * @param type The JSONTokenType of the token
+		 * @param value The value of the token
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public function JSONToken( type:int = -1 /* JSONTokenType.UNKNOWN */, value:Object = null ) {
+			_type = type;
+			_value = value;
+		}
+
+		/**
+		 * Returns the type of the token.
+		 *
+		 * @see com.adobe.serialization.json.JSONTokenType
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public function get type():int {
+			return _type;
+		}
+
+		/**
+		 * Sets the type of the token.
+		 *
+		 * @see com.adobe.serialization.json.JSONTokenType
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public function set type( value:int ):void {
+			_type = value;
+		}
+
+		/**
+		 * Gets the value of the token
+		 *
+		 * @see com.adobe.serialization.json.JSONTokenType
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public function get value():Object {
+			return _value;
+		}
+
+		/**
+		 * Sets the value of the token
+		 *
+		 * @see com.adobe.serialization.json.JSONTokenType
+		 * @langversion ActionScript 3.0
+		 * @playerversion Flash 9.0
+		 * @tiptext
+		 */
+		public function set value ( v:Object ):void {
+			_value = v;
+		}
+
+	}
+
+}
\ No newline at end of file
diff --git a/src/flexchart/com/adobe/serialization/json/JSONTokenType.as b/src/flexchart/com/adobe/serialization/json/JSONTokenType.as
new file mode 100644
index 0000000..adaa0d7
--- /dev/null
+++ b/src/flexchart/com/adobe/serialization/json/JSONTokenType.as
@@ -0,0 +1,67 @@
+/*
+  Copyright (c) 2008, Adobe Systems Incorporated
+  All rights reserved.
+
+  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.
+
+  * Neither the name of Adobe Systems Incorporated nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR
+  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.
+*/
+
+package com.adobe.serialization.json {
+
+	/**
+	 * Class containing constant values for the different types
+	 * of tokens in a JSON encoded string.
+	 */
+	public class JSONTokenType {
+
+		public static const UNKNOWN:int = -1;
+
+		public static const COMMA:int = 0;
+
+		public static const LEFT_BRACE:int = 1;
+
+		public static const RIGHT_BRACE:int = 2;
+
+		public static const LEFT_BRACKET:int = 3;
+
+		public static const RIGHT_BRACKET:int = 4;
+
+		public static const COLON:int = 6;
+
+		public static const TRUE:int = 7;
+
+		public static const FALSE:int = 8;
+
+		public static const NULL:int = 9;
+
+		public static const STRING:int = 10;
+
+		public static const NUMBER:int = 11;
+
+	}
+
+}
\ No newline at end of file
diff --git a/src/flexchart/com/adobe/serialization/json/JSONTokenizer.as b/src/flexchart/com/adobe/serialization/json/JSONTokenizer.as
new file mode 100644
index 0000000..9e7a5a4
--- /dev/null
+++ b/src/flexchart/com/adobe/serialization/json/JSONTokenizer.as
@@ -0,0 +1,547 @@
+/*
+  Copyright (c) 2008, Adobe Systems Incorporated
+  All rights reserved.
+
+  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.
+
+  * Neither the name of Adobe Systems Incorporated nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR
+  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.
+*/
+
+package com.adobe.serialization.json {
+
+	public class JSONTokenizer {
+
+		/** The object that will get parsed from the JSON string */
+		private var obj:Object;
+
+		/** The JSON string to be parsed */
+		private var jsonString:String;
+
+		/** The current parsing location in the JSON string */
+		private var loc:int;
+
+		/** The current character in the JSON string during parsing */
+		private var ch:String;
+
+		/**
+		 * Constructs a new JSONDecoder to parse a JSON string
+		 * into a native object.
+		 *
+		 * @param s The JSON string to be converted
+		 *		into a native object
+		 */
+		public function JSONTokenizer( s:String ) {
+			jsonString = s;
+			loc = 0;
+
+			// prime the pump by getting the first character
+			nextChar();
+		}
+
+		/**
+		 * Gets the next token in the input sting and advances
+		* the character to the next character after the token
+		 */
+		public function getNextToken():JSONToken {
+			var token:JSONToken = new JSONToken();
+
+			// skip any whitespace / comments since the last
+			// token was read
+			skipIgnored();
+
+			// examine the new character and see what we have...
+			switch ( ch ) {
+
+				case '{':
+					token.type = JSONTokenType.LEFT_BRACE;
+					token.value = '{';
+					nextChar();
+					break
+
+				case '}':
+					token.type = JSONTokenType.RIGHT_BRACE;
+					token.value = '}';
+					nextChar();
+					break
+
+				case '[':
+					token.type = JSONTokenType.LEFT_BRACKET;
+					token.value = '[';
+					nextChar();
+					break
+
+				case ']':
+					token.type = JSONTokenType.RIGHT_BRACKET;
+					token.value = ']';
+					nextChar();
+					break
+
+				case ',':
+					token.type = JSONTokenType.COMMA;
+					token.value = ',';
+					nextChar();
+					break
+
+				case ':':
+					token.type = JSONTokenType.COLON;
+					token.value = ':';
+					nextChar();
+					break;
+
+				case 't': // attempt to read true
+					var possibleTrue:String = "t" + nextChar() + nextChar() + nextChar();
+
+					if ( possibleTrue == "true" ) {
+						token.type = JSONTokenType.TRUE;
+						token.value = true;
+						nextChar();
+					} else {
+						parseError( "Expecting 'true' but found " + possibleTrue );
+					}
+
+					break;
+
+				case 'f': // attempt to read false
+					var possibleFalse:String = "f" + nextChar() + nextChar() + nextChar() + nextChar();
+
+					if ( possibleFalse == "false" ) {
+						token.type = JSONTokenType.FALSE;
+						token.value = false;
+						nextChar();
+					} else {
+						parseError( "Expecting 'false' but found " + possibleFalse );
+					}
+
+					break;
+
+				case 'n': // attempt to read null
+
+					var possibleNull:String = "n" + nextChar() + nextChar() + nextChar();
+
+					if ( possibleNull == "null" ) {
+						token.type = JSONTokenType.NULL;
+						token.value = null;
+						nextChar();
+					} else {
+						parseError( "Expecting 'null' but found " + possibleNull );
+					}
+
+					break;
+
+				case '"': // the start of a string
+					token = readString();
+					break;
+
+				default:
+					// see if we can read a number
+					if ( isDigit( ch ) || ch == '-' ) {
+						token = readNumber();
+					} else if ( ch == '' ) {
+						// check for reading past the end of the string
+						return null;
+					} else {
+						// not sure what was in the input string - it's not
+						// anything we expected
+						parseError( "Unexpected " + ch + " encountered" );
+					}
+			}
+
+			return token;
+		}
+
+		/**
+		 * Attempts to read a string from the input string.  Places
+		 * the character location at the first character after the
+		 * string.  It is assumed that ch is " before this method is called.
+		 *
+		 * @return the JSONToken with the string value if a string could
+		 *		be read.  Throws an error otherwise.
+		 */
+		private function readString():JSONToken {
+			// the token for the string we'll try to read
+			var token:JSONToken = new JSONToken();
+			token.type = JSONTokenType.STRING;
+
+			// the string to store the string we'll try to read
+			var string:String = "";
+
+			// advance past the first "
+			nextChar();
+
+			while ( ch != '"' && ch != '' ) {
+
+				// unescape the escape sequences in the string
+				if ( ch == '\\' ) {
+
+					// get the next character so we know what
+					// to unescape
+					nextChar();
+
+					switch ( ch ) {
+
+						case '"': // quotation mark
+							string += '"';
+							break;
+
+						case '/':	// solidus
+							string += "/";
+							break;
+
+						case '\\':	// reverse solidus
+							string += '\\';
+							break;
+
+						case 'b':	// bell
+							string += '\b';
+							break;
+
+						case 'f':	// form feed
+							string += '\f';
+							break;
+
+						case 'n':	// newline
+							string += '\n';
+							break;
+
+						case 'r':	// carriage return
+							string += '\r';
+							break;
+
+						case 't':	// horizontal tab
+							string += '\t'
+							break;
+
+						case 'u':
+							// convert a unicode escape sequence
+							// to it's character value - expecting
+							// 4 hex digits
+
+							// save the characters as a string we'll convert to an int
+							var hexValue:String = "";
+
+							// try to find 4 hex characters
+							for ( var i:int = 0; i < 4; i++ ) {
+								// get the next character and determine
+								// if it's a valid hex digit or not
+								if ( !isHexDigit( nextChar() ) ) {
+									parseError( " Excepted a hex digit, but found: " + ch );
+								}
+								// valid, add it to the value
+								hexValue += ch;
+							}
+
+							// convert hexValue to an integer, and use that
+							// integrer value to create a character to add
+							// to our string.
+							string += String.fromCharCode( parseInt( hexValue, 16 ) );
+
+							break;
+
+						default:
+							// couldn't unescape the sequence, so just
+							// pass it through
+							string += '\\' + ch;
+
+					}
+
+				} else {
+					// didn't have to unescape, so add the character to the string
+					string += ch;
+
+				}
+
+				// move to the next character
+				nextChar();
+
+			}
+
+			// we read past the end of the string without closing it, which
+			// is a parse error
+			if ( ch == '' ) {
+				parseError( "Unterminated string literal" );
+			}
+
+			// move past the closing " in the input string
+			nextChar();
+
+			// attach to the string to the token so we can return it
+			token.value = string;
+
+			return token;
+		}
+
+		/**
+		 * Attempts to read a number from the input string.  Places
+		 * the character location at the first character after the
+		 * number.
+		 *
+		 * @return The JSONToken with the number value if a number could
+		 * 		be read.  Throws an error otherwise.
+		 */
+		private function readNumber():JSONToken {
+			// the token for the number we'll try to read
+			var token:JSONToken = new JSONToken();
+			token.type = JSONTokenType.NUMBER;
+
+			// the string to accumulate the number characters
+			// into that we'll convert to a number at the end
+			var input:String = "";
+
+			// check for a negative number
+			if ( ch == '-' ) {
+				input += '-';
+				nextChar();
+			}
+
+			// the number must start with a digit
+			if ( !isDigit( ch ) )
+			{
+				parseError( "Expecting a digit" );
+			}
+
+			// 0 can only be the first digit if it
+			// is followed by a decimal point
+			if ( ch == '0' )
+			{
+				input += ch;
+				nextChar();
+
+				// make sure no other digits come after 0
+				if ( isDigit( ch ) )
+				{
+					parseError( "A digit cannot immediately follow 0" );
+				}
+			}
+			else
+			{
+				// read numbers while we can
+				while ( isDigit( ch ) ) {
+					input += ch;
+					nextChar();
+				}
+			}
+
+			// check for a decimal value
+			if ( ch == '.' ) {
+				input += '.';
+				nextChar();
+
+				// after the decimal there has to be a digit
+				if ( !isDigit( ch ) )
+				{
+					parseError( "Expecting a digit" );
+				}
+
+				// read more numbers to get the decimal value
+				while ( isDigit( ch ) ) {
+					input += ch;
+					nextChar();
+				}
+			}
+
+			// check for scientific notation
+			if ( ch == 'e' || ch == 'E' )
+			{
+				input += "e"
+				nextChar();
+				// check for sign
+				if ( ch == '+' || ch == '-' )
+				{
+					input += ch;
+					nextChar();
+				}
+
+				// require at least one number for the exponent
+				// in this case
+				if ( !isDigit( ch ) )
+				{
+					parseError( "Scientific notation number needs exponent value" );
+				}
+
+				// read in the exponent
+				while ( isDigit( ch ) )
+				{
+					input += ch;
+					nextChar();
+				}
+			}
+
+			// convert the string to a number value
+			var num:Number = Number( input );
+
+			if ( isFinite( num ) && !isNaN( num ) ) {
+				token.value = num;
+				return token;
+			} else {
+				parseError( "Number " + num + " is not valid!" );
+			}
+            return null;
+		}
+
+		/**
+		 * Reads the next character in the input
+		 * string and advances the character location.
+		 *
+		 * @return The next character in the input string, or
+		 *		null if we've read past the end.
+		 */
+		private function nextChar():String {
+			return ch = jsonString.charAt( loc++ );
+		}
+
+		/**
+		 * Advances the character location past any
+		 * sort of white space and comments
+		 */
+		private function skipIgnored():void {
+			skipWhite();
+			skipComments();
+			skipWhite();
+		}
+
+		/**
+		 * Skips comments in the input string, either
+		 * single-line or multi-line.  Advances the character
+		 * to the first position after the end of the comment.
+		 */
+		private function skipComments():void {
+			if ( ch == '/' ) {
+				// Advance past the first / to find out what type of comment
+				nextChar();
+				switch ( ch ) {
+					case '/': // single-line comment, read through end of line
+
+						// Loop over the characters until we find
+						// a newline or until there's no more characters left
+						do {
+							nextChar();
+						} while ( ch != '\n' && ch != '' )
+
+						// move past the \n
+						nextChar();
+
+						break;
+
+					case '*': // multi-line comment, read until closing */
+
+						// move past the opening *
+						nextChar();
+
+						// try to find a trailing */
+						while ( true ) {
+							if ( ch == '*' ) {
+								// check to see if we have a closing /
+								nextChar();
+								if ( ch == '/') {
+									// move past the end of the closing */
+									nextChar();
+									break;
+								}
+							} else {
+								// move along, looking if the next character is a *
+								nextChar();
+							}
+
+							// when we're here we've read past the end of
+							// the string without finding a closing */, so error
+							if ( ch == '' ) {
+								parseError( "Multi-line comment not closed" );
+							}
+						}
+
+						break;
+
+					// Can't match a comment after a /, so it's a parsing error
+					default:
+						parseError( "Unexpected " + ch + " encountered (expecting '/' or '*' )" );
+				}
+			}
+
+		}
+
+
+		/**
+		 * Skip any whitespace in the input string and advances
+		 * the character to the first character after any possible
+		 * whitespace.
+		 */
+		private function skipWhite():void {
+
+			// As long as there are spaces in the input
+			// stream, advance the current location pointer
+			// past them
+			while ( isWhiteSpace( ch ) ) {
+				nextChar();
+			}
+
+		}
+
+		/**
+		 * Determines if a character is whitespace or not.
+		 *
+		 * @return True if the character passed in is a whitespace
+		 *	character
+		 */
+		private function isWhiteSpace( ch:String ):Boolean {
+			return ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' );
+		}
+
+		/**
+		 * Determines if a character is a digit [0-9].
+		 *
+		 * @return True if the character passed in is a digit
+		 */
+		private function isDigit( ch:String ):Boolean {
+			return ( ch >= '0' && ch <= '9' );
+		}
+
+		/**
+		 * Determines if a character is a digit [0-9].
+		 *
+		 * @return True if the character passed in is a digit
+		 */
+		private function isHexDigit( ch:String ):Boolean {
+			// get the uppercase value of ch so we only have
+			// to compare the value between 'A' and 'F'
+			var uc:String = ch.toUpperCase();
+
+			// a hex digit is a digit of A-F, inclusive ( using
+			// our uppercase constraint )
+			return ( isDigit( ch ) || ( uc >= 'A' && uc <= 'F' ) );
+		}
+
+		/**
+		 * Raises a parsing error with a specified message, tacking
+		 * on the error location and the original string.
+		 *
+		 * @param message The message indicating why the error occurred
+		 */
+		public function parseError( message:String ):void {
+			throw new JSONParseError( message, loc, jsonString );
+		}
+	}
+
+}
diff --git a/src/flexchart/flexchart.mxml b/src/flexchart/flexchart.mxml
new file mode 100644
index 0000000..796329d
--- /dev/null
+++ b/src/flexchart/flexchart.mxml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" applicationComplete="populate(flexChart)">
+  <mx:Script>
+    <![CDATA[
+
+      import mx.containers.Box;
+      import org.ovirt.*;
+
+      private function populate(chart:Box):void {
+        var chartLoader:ChartLoader = new ChartLoader(chart, parameters['flexchart_data']);
+        chartLoader.load();
+      }
+
+    ]]>
+  </mx:Script>
+  <mx:Panel height="100%" width="100%" visible="true">
+    <mx:HBox id="flexChart"  height="100%" width="100%" visible="true" verticalAlign="bottom" opaqueBackground="0xFFFFFF" borderThickness="0">
+    </mx:HBox>
+  </mx:Panel>
+</mx:Application>
diff --git a/src/flexchart/org/ovirt/ChartLoader.as b/src/flexchart/org/ovirt/ChartLoader.as
new file mode 100644
index 0000000..4e493a4
--- /dev/null
+++ b/src/flexchart/org/ovirt/ChartLoader.as
@@ -0,0 +1,64 @@
+/*
+ Copyright (C) 2008 Red Hat, Inc.
+ Written by Steve Linabery <slinabery at redhat.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA  02110-1301, USA.  A copy of the GNU General Public License is
+ also available at http://www.gnu.org/copyleft/gpl.html.
+*/
+
+package org.ovirt {
+
+  import mx.containers.Box;
+  import mx.containers.HBox;
+  import mx.controls.Text;
+
+  public class ChartLoader {
+
+    private var element:Box;
+    private var datasourceUrl:String;
+
+    public function ChartLoader(element:Box, datasourceUrl:String) {
+      this.element = element;
+      this.datasourceUrl = datasourceUrl;
+    }
+
+    public function addData(dataSeries:DataSeries):void {
+      var points:Array = dataSeries.getPoints();
+      var maxValue:Number = dataSeries.getMaxValue();
+      var scale:Number = maxValue;
+      if (scale == 0) { scale = 1; }
+      var size:int = points.length;
+      element.removeAllChildren();
+      element.setStyle("horizontalGap","2");
+      for (var i:int = 0; i < size; i++) {
+        var value:Number = (points[i] as Array)[1];
+        var bar:HBox = new HBox();
+        bar.percentHeight = ((value / scale) * 90);
+        bar.percentWidth = (100 / size);
+        bar.setStyle("backgroundColor","0x0000FF");
+        bar.setStyle("left","1");
+        bar.setStyle("right","1");
+        bar.visible = true;
+        bar.setVisible(true);
+        element.addChild(bar);
+      }
+    }
+
+    public function load():void {
+      var dataSource:DataSource = new DataSource(this);
+      dataSource.retrieveData(datasourceUrl);
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/flexchart/org/ovirt/DataSeries.as b/src/flexchart/org/ovirt/DataSeries.as
new file mode 100644
index 0000000..d63162a
--- /dev/null
+++ b/src/flexchart/org/ovirt/DataSeries.as
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 2008 Red Hat, Inc.
+ Written by Steve Linabery <slinabery at redhat.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA  02110-1301, USA.  A copy of the GNU General Public License is
+ also available at http://www.gnu.org/copyleft/gpl.html.
+*/
+
+//class to encapsulate the json object representation of a data
+//series returned from stats package
+
+package org.ovirt {
+
+  public class DataSeries {
+
+    private var object:Object;
+
+    public function DataSeries (object:Object) {
+      this.object = object;
+    }
+
+    public function getPoints():Array {
+      return object["vectors"] as Array;
+    }
+
+    public function getMaxValue():Number {
+      return object["max_value"] as Number;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/flexchart/org/ovirt/DataSource.as b/src/flexchart/org/ovirt/DataSource.as
new file mode 100644
index 0000000..1a64f03
--- /dev/null
+++ b/src/flexchart/org/ovirt/DataSource.as
@@ -0,0 +1,57 @@
+/*
+ Copyright (C) 2008 Red Hat, Inc.
+ Written by Steve Linabery <slinabery at redhat.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA  02110-1301, USA.  A copy of the GNU General Public License is
+ also available at http://www.gnu.org/copyleft/gpl.html.
+*/
+
+package org.ovirt {
+
+  import flash.net.URLLoader;
+  import flash.net.URLRequest;
+  import com.adobe.serialization.json.JSON;
+  import flash.events.Event;
+  import flash.events.IOErrorEvent;
+
+  public class DataSource {
+
+    private var chartLoader:ChartLoader;
+
+    public function DataSource(chartLoader:ChartLoader) {
+      this.chartLoader = chartLoader;
+    }
+
+    public function retrieveData(url:String):void {
+      var loader:URLLoader = new URLLoader();
+      loader.addEventListener( IOErrorEvent.IO_ERROR, this.ioError );
+      loader.addEventListener( Event.COMPLETE, dataLoaded );
+      var request:URLRequest = new URLRequest(url);
+      loader.load(request);
+    }
+
+    private function dataLoaded(event:Event):void {
+      var loader:URLLoader = URLLoader(event.target);
+      var object:Object = JSON.decode(loader.data);
+      var series:DataSeries = new DataSeries(object);
+      chartLoader.addData(series);
+    }
+
+    private function ioError( e:IOErrorEvent ):void {
+      //FIXME:
+      //do something useful with this error
+    }
+  }
+}
diff --git a/src/public/javascripts/jquery.flash.js b/src/public/javascripts/jquery.flash.js
new file mode 100644
index 0000000..2608834
--- /dev/null
+++ b/src/public/javascripts/jquery.flash.js
@@ -0,0 +1,288 @@
+/**
+ * Flash (http://jquery.lukelutman.com/plugins/flash)
+ * A jQuery plugin for embedding Flash movies.
+ *
+ * Version 1.0
+ * November 9th, 2006
+ *
+ * Copyright (c) 2006 Luke Lutman (http://www.lukelutman.com)
+ * Dual licensed under the MIT and GPL licenses.
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.opensource.org/licenses/gpl-license.php
+ *
+ * Inspired by:
+ * SWFObject (http://blog.deconcept.com/swfobject/)
+ * UFO (http://www.bobbyvandersluis.com/ufo/)
+ * sIFR (http://www.mikeindustries.com/sifr/)
+ *
+ * IMPORTANT:
+ * The packed version of jQuery breaks ActiveX control
+ * activation in Internet Explorer. Use JSMin to minifiy
+ * jQuery (see: http://jquery.lukelutman.com/plugins/flash#activex).
+ *
+ **/
+;(function(){
+
+var $$;
+
+/**
+ *
+ * @desc Replace matching elements with a flash movie.
+ * @author Luke Lutman
+ * @version 1.0.1
+ *
+ * @name flash
+ * @param Hash htmlOptions Options for the embed/object tag.
+ * @param Hash pluginOptions Options for detecting/updating the Flash plugin (optional).
+ * @param Function replace Custom block called for each matched element if flash is installed (optional).
+ * @param Function update Custom block called for each matched if flash isn't installed (optional).
+ * @type jQuery
+ *
+ * @cat plugins/flash
+ *
+ * @example $('#hello').flash({ src: 'hello.swf' });
+ * @desc Embed a Flash movie.
+ *
+ * @example $('#hello').flash({ src: 'hello.swf' }, { version: 8 });
+ * @desc Embed a Flash 8 movie.
+ *
+ * @example $('#hello').flash({ src: 'hello.swf' }, { expressInstall: true });
+ * @desc Embed a Flash movie using Express Install if flash isn't installed.
+ *
+ * @example $('#hello').flash({ src: 'hello.swf' }, { update: false });
+ * @desc Embed a Flash movie, don't show an update message if Flash isn't installed.
+ *
+**/
+$$ = jQuery.fn.flash = function(htmlOptions, pluginOptions, replace, update) {
+
+	// Set the default block.
+	var block = replace || $$.replace;
+
+	// Merge the default and passed plugin options.
+	pluginOptions = $$.copy($$.pluginOptions, pluginOptions);
+
+	// Detect Flash.
+	if(!$$.hasFlash(pluginOptions.version)) {
+		// Use Express Install (if specified and Flash plugin 6,0,65 or higher is installed).
+		if(pluginOptions.expressInstall && $$.hasFlash(6,0,65)) {
+			// Add the necessary flashvars (merged later).
+			var expressInstallOptions = {
+				flashvars: {
+					MMredirectURL: location,
+					MMplayerType: 'PlugIn',
+					MMdoctitle: jQuery('title').text()
+				}
+			};
+		// Ask the user to update (if specified).
+		} else if (pluginOptions.update) {
+			// Change the block to insert the update message instead of the flash movie.
+			block = update || $$.update;
+		// Fail
+		} else {
+			// The required version of flash isn't installed.
+			// Express Install is turned off, or flash 6,0,65 isn't installed.
+			// Update is turned off.
+			// Return without doing anything.
+			return this;
+		}
+	}
+
+	// Merge the default, express install and passed html options.
+	htmlOptions = $$.copy($$.htmlOptions, expressInstallOptions, htmlOptions);
+
+	// Invoke $block (with a copy of the merged html options) for each element.
+	return this.each(function(){
+		block.call(this, $$.copy(htmlOptions));
+	});
+
+};
+/**
+ *
+ * @name flash.copy
+ * @desc Copy an arbitrary number of objects into a new object.
+ * @type Object
+ *
+ * @example $$.copy({ foo: 1 }, { bar: 2 });
+ * @result { foo: 1, bar: 2 };
+ *
+**/
+$$.copy = function() {
+	var options = {}, flashvars = {};
+	for(var i = 0; i < arguments.length; i++) {
+		var arg = arguments[i];
+		if(arg == undefined) continue;
+		jQuery.extend(options, arg);
+		// don't clobber one flash vars object with another
+		// merge them instead
+		if(arg.flashvars == undefined) continue;
+		jQuery.extend(flashvars, arg.flashvars);
+	}
+	options.flashvars = flashvars;
+	return options;
+};
+/*
+ * @name flash.hasFlash
+ * @desc Check if a specific version of the Flash plugin is installed
+ * @type Boolean
+ *
+**/
+$$.hasFlash = function() {
+	// look for a flag in the query string to bypass flash detection
+	if(/hasFlash\=true/.test(location)) return true;
+	if(/hasFlash\=false/.test(location)) return false;
+	var pv = $$.hasFlash.playerVersion().match(/\d+/g);
+	var rv = String([arguments[0], arguments[1], arguments[2]]).match(/\d+/g) || String($$.pluginOptions.version).match(/\d+/g);
+	for(var i = 0; i < 3; i++) {
+		pv[i] = parseInt(pv[i] || 0);
+		rv[i] = parseInt(rv[i] || 0);
+		// player is less than required
+		if(pv[i] < rv[i]) return false;
+		// player is greater than required
+		if(pv[i] > rv[i]) return true;
+	}
+	// major version, minor version and revision match exactly
+	return true;
+};
+/**
+ *
+ * @name flash.hasFlash.playerVersion
+ * @desc Get the version of the installed Flash plugin.
+ * @type String
+ *
+**/
+$$.hasFlash.playerVersion = function() {
+	// ie
+	try {
+		try {
+			// avoid fp6 minor version lookup issues
+			// see: http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internet-explorer-flash-6/
+			var axo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6');
+			try { axo.AllowScriptAccess = 'always';	}
+			catch(e) { return '6,0,0'; }
+		} catch(e) {}
+		return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
+	// other browsers
+	} catch(e) {
+		try {
+			if(navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){
+				return (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1];
+			}
+		} catch(e) {}
+	}
+	return '0,0,0';
+};
+/**
+ *
+ * @name flash.htmlOptions
+ * @desc The default set of options for the object or embed tag.
+ *
+**/
+$$.htmlOptions = {
+	height: 240,
+	flashvars: {},
+	pluginspage: 'http://www.adobe.com/go/getflashplayer',
+	src: '#',
+	type: 'application/x-shockwave-flash',
+	width: 320
+};
+/**
+ *
+ * @name flash.pluginOptions
+ * @desc The default set of options for checking/updating the flash Plugin.
+ *
+**/
+$$.pluginOptions = {
+	expressInstall: false,
+	update: true,
+	version: '6.0.65'
+};
+/**
+ *
+ * @name flash.replace
+ * @desc The default method for replacing an element with a Flash movie.
+ *
+**/
+$$.replace = function(htmlOptions) {
+	this.innerHTML = '<div class="alt">'+this.innerHTML+'</div>';
+	jQuery(this)
+		.addClass('flash-replaced')
+		.prepend($$.transform(htmlOptions));
+};
+/**
+ *
+ * @name flash.update
+ * @desc The default method for replacing an element with an update message.
+ *
+**/
+$$.update = function(htmlOptions) {
+	var url = String(location).split('?');
+	url.splice(1,0,'?hasFlash=true&');
+	url = url.join('');
+	var msg = '<p>This content requires the Flash Player. <a href="http://www.adobe.com/go/getflashplayer">Download Flash Player</a>. Already have Flash Player? <a href="'+url+'">Click here.</a></p>';
+	this.innerHTML = '<span class="alt">'+this.innerHTML+'</span>';
+	jQuery(this)
+		.addClass('flash-update')
+		.prepend(msg);
+};
+/**
+ *
+ * @desc Convert a hash of html options to a string of attributes, using Function.apply().
+ * @example toAttributeString.apply(htmlOptions)
+ * @result foo="bar" foo="bar"
+ *
+**/
+function toAttributeString() {
+	var s = '';
+	for(var key in this)
+		if(typeof this[key] != 'function')
+			s += key+'="'+this[key]+'" ';
+	return s;
+};
+/**
+ *
+ * @desc Convert a hash of flashvars to a url-encoded string, using Function.apply().
+ * @example toFlashvarsString.apply(flashvarsObject)
+ * @result foo=bar&foo=bar
+ *
+**/
+function toFlashvarsString() {
+	var s = '';
+	for(var key in this)
+		if(typeof this[key] != 'function')
+			s += key+'='+encodeURIComponent(this[key])+'&';
+	return s.replace(/&$/, '');
+};
+/**
+ *
+ * @name flash.transform
+ * @desc Transform a set of html options into an embed tag.
+ * @type String
+ *
+ * @example $$.transform(htmlOptions)
+ * @result <embed src="foo.swf" ... />
+ *
+ * Note: The embed tag is NOT standards-compliant, but it
+ * works in all current browsers. flash.transform can be
+ * overwritten with a custom function to generate more
+ * standards-compliant markup.
+ *
+**/
+$$.transform = function(htmlOptions) {
+	htmlOptions.toString = toAttributeString;
+	if(htmlOptions.flashvars) htmlOptions.flashvars.toString = toFlashvarsString;
+	return '<embed ' + String(htmlOptions) + '/>';
+};
+
+/**
+ *
+ * Flash Player 9 Fix (http://blog.deconcept.com/2006/07/28/swfobject-143-released/)
+ *
+**/
+if (window.attachEvent) {
+	window.attachEvent("onbeforeunload", function(){
+		__flash_unloadHandler = function() {};
+		__flash_savedUnloadHandler = function() {};
+	});
+}
+
+})();
\ No newline at end of file
-- 
1.5.5.1



More information about the ovirt-devel mailing list