#! /usr/bin/env python
"""
swfcharts.py
Version: 1.0
Wrapper class for XML/SWF Charts
http://www.maani.us/xml_charts/index.php
Written by: Justin Israel (justinisrael@gmail.com)
June 2009
"""
import re
from collections import defaultdict
#############################################################
#############################################################
######## SwfChart
#############################################################
#############################################################
class SwfChartException(Exception):
""" Exception for SwfChart class """
pass
class SwfChart(object) :
"""
SUMMARY:
A wrapper class around the XML/SWF Charts XML api.
Represents the attributes and data model for a graph
and will output the proper XML data to pass into the
flash SWF Charts application.
All class properties correspond to the existing properties
from the XML api, and return the formatted XML code based
on the attributes that were set with the methods. Once you
set all your desired settings using the methods, the class
can be printed or treated like a string to represent the
entire XML document, or you can call getXML()
Some of the methods take a very large list of optional
key/value arguments. The available args are listed
in each method, but for detailed descriptions of the
attributes, please refer to the XML Charts documentation:
http://www.maani.us/xml_charts/index.php?menu=Reference
"""
def __init__(self):
"""
__init__()
"""
self._setProperties = []
self._properties = defaultdict(str)
self._rowLength = 0
self._colLength = 0
def __str__(self):
"""
The class instance string representation is the complete
XML output, from all currently set attributes.
"""
chart = ['']
for prop in self._setProperties:
chart.append(getattr(self, prop))
chart.append('')
chartStr = '\n'.join(chart)
chartStr = re.sub(r'\n+', r'\n', chartStr)
return chartStr
def _setPropertySet(self, prop):
"""
ACTION:
Indicate that a property has been set, and should be included
when the XML is printed
INPUT: str prop - a property of the class, that is being marked as set
OUTPUT: NONE
"""
if not prop in self._setProperties:
self._setProperties.append(prop)
def _setPropertyFromDict(self, prop, **d):
"""
ACTION:
Private method takes a dictionary of kwargs, sets them as XML under the given property name,
and marks the property as set for the final XML output
INPUT:
str prop - a property to set the values of
dict d - key/value pairs to set as attributes in the XML tag
OUTPUT: NONE
"""
if not d:
return
chartStr = '\t<%s' % prop
for k,v in d.iteritems():
chartStr += "\n\t\t %s='%s'" % (str(k), str(v))
chartStr += '\n\t\t />'
self._properties[prop] = chartStr
self._setPropertySet(prop)
#########################
# Properties
#
# Each property reflects the properties in the XML Charts api
# and returns the block of xml formatted from setting the methods
#
@property
def license(self):
return self._properties['license']
@property
def chart_border(self):
return self._properties['chart_border']
@property
def chart_data(self):
"""
Returns the formatted xml section
"""
rows = self._properties['rows']
if not rows:
return ''
cols = self._properties['cols']
if self._colLength < self._rowLength:
# there must be the same number of cols as row data
for i in xrange(self._rowLength - self._colLength):
cols += "\n\t\t\tUndef"
colStr = "\t\t\n\t\t\t\n%s\n\t\t" % cols
chartData = ["\t"]
chartData.append(colStr)
chartData.append(rows)
chartData.append("\t")
return '\n'.join(chartData)
@property
def chart_grid_h(self):
return self._properties['chart_grid_h']
@property
def chart_grid_v(self):
return self._properties['chart_grid_v']
@property
def chart_guide(self):
return self._properties['chart_guide']
@property
def chart_label(self):
return self._properties['chart_label']
@property
def chart_note(self):
return self._properties['chart_note']
@property
def chart_pref(self):
return self._properties['chart_pref']
@property
def chart_rect(self):
return self._properties['chart_rect']
@property
def chart_transition(self):
return self._properties['chart_transition']
@property
def chart_type(self):
return self._properties['chart_type']
@property
def series(self):
return self._properties['series']
@property
def series_color(self):
return self._properties['series_color']
@property
def series_explode(self):
return self._properties['series_explode']
@property
def axis_category(self):
return self._properties['axis_category']
@property
def axis_category_label(self):
return self._properties['axis_category_label']
@property
def axis_ticks(self):
return self._properties['axis_ticks']
@property
def axis_value(self):
return self._properties['axis_value']
@property
def axis_value_label(self):
return self._properties['axis_value_label']
@property
def draw(self):
"""
returns the formatted XML element
"""
dStr = self._properties['drawElements']
if not dStr:
return ''
drawData = ['\t']
drawData.append(dStr)
drawData.append('\t')
drawStr = '\n'.join(drawData)
return drawStr
@property
def filter(self):
"""
returns the formatted XML element
"""
fStr = self._properties['filters']
if not fStr:
return ''
filterData = ['\t']
filterData.append(fStr)
filterData.append('\t')
filterStr = '\n'.join(filterData)
return filterStr
@property
def context_menu(self):
return self._properties['context_menu']
@property
def legend(self):
return self._properties['legend']
@property
def link(self):
"""
returns the formatted XML element
"""
lStr = self._properties['links']
if not lStr:
return ''
linkData = ['\t']
linkData.append(lStr)
linkData.append('\t')
linkStr = '\n'.join(linkData)
return linkStr
@property
def link_data(self):
return self._properties['link_data']
@property
def scroll(self):
return self._properties['scroll']
@property
def tooltip(self):
return self._properties['tooltip']
@property
def update(self):
return self._properties['update']
# End Properties
#########################
def addColumnLabel(self, *labels):
"""
ACTION:
Add one or more string labels for the columns in the graph. There
needs to be enough column labels to correspond to the length of the data
in the rows. Otherwise, they will be labeled "Undef".
INPUT: str label, [str label], [...]
OUTPUT: NONE
"""
self._colLength += len(labels)
colData = []
for label in labels:
colData.append("\t\t\t%s" % label)
colStr = '\n'.join(colData)
self._properties['cols'] = '\n'.join( (self._properties['cols'], colStr) )
def addDrawElement(self, element, **kwargs):
"""
ACTION:
Creates a new draw element in the graph. Draw elements are
a few defined graphic objects that can be inserted into either
the background or foreground of the graph. The draw elements
each have their own respective attributes for controlling how
and where they are displayed.
For element type 'text', specify the text with the key 'value'.
So, value='some text'
Reference the API doc for details on each elements types
attributes.
INPUT:
str element - (circle, image, line, rect, text, button)
Key=Value - attributes for the draw element type
OUTPUT: NONE
"""
elementTypes = ('circle', 'image', 'line', 'rect', 'text', 'button')
if not element in elementTypes:
raise SwfChartException(
"Element type '%s' is not one of the valid types in: %s" % (element, elementTypes))
chartStr = '\t\t<%s' % element
for k,v in kwargs.iteritems():
if not k=='url':
val = str(v).lower()
else:
val = str(v)
chartStr += "\n\t\t\t %s='%s'" % (k, val)
if element == 'text':
chartStr += '\n\t\t\t>%s' % kwargs.get('value', '')
else:
chartStr += '\n\t\t\t />'
self._properties['drawElements'] = '\n'.join( (self._properties['drawElements'], chartStr) )
self._setPropertySet('draw')
def addFilter(self, filterType, id, **kwargs):
"""
ACTION:
Adds a new filter to the list of defined filters.
Valid filter types are shadow, bevel, glow, and blur.
You can defined multiple types of each, and give them
unique ids, to be referenced in other methods.
Each filter type has its own set of key/value attributes,
so refer to the API doc for specifics.
INPUT:
str filterType - 'shadow', 'bevel', 'glow', or 'blur'
str id - a unique string id to be known as ('shadow1')
key = value - attributes for the specific filter type
OUTPUT: NONE
"""
filterTypes = ('shadow', 'bevel', 'glow', 'blur')
if not filterType in filterTypes:
raise SwfChartException(
"mode '%s' is not one of the allowed types: %s" % (filterType, filterTypes))
chartStr = '\t\t<%s' % filterType
chartStr += "\n\t\t\t id='%s'" % id
for k,v in kwargs.iteritems():
chartStr += "\n\t\t\t %s='%s'" % (k, str(v).lower())
chartStr += '\n\t\t\t />'
self._properties['filters'] = '\n'.join( (self._properties['filters'], chartStr) )
self._setPropertySet('filter')
def addLink(self, **kwargs):
"""
ACTION:
holds any number of areas, each defining a rectangle and a
URL to go to when the user clicks inside the rectangle.
This can also be used to assign functions to mouse clicks,
including chart updates, printing, etc
INPUT:
int x - x position of upper left corner of rectangle
int y - y position of upper left corner of rectange
int width - width of rectangle
int height - height of rectable
str url - relative or absolute URL to go to when clicked
int priority - determined how to handle links that overlap
if 0, priority is determined by order of links
if 1, link will be activated during overlap
str target - where to open the URL when clicked
Window options: (_self, _blank, _parent, _top)
Other options: (update, print, toggle_fullscreen, scroll)
* If any other value is specified, the URL will
be opened in a window named . If it does not
exist, it will be created first.
str tooltip - tooltip text to display over link
* Valid if target = 'update'
int timeout - time to wait for update before failing; seconds
int retry - number of times to retry when failing
bool spinning_wheel - if True, show a spinning wheel while updating
OUTPUT: NONE
"""
chartStr = '\t\t'
self._properties['links'] = '\n'.join( (self._properties['links'], chartStr) )
self._setPropertySet('link')
def addRow(self, label, *data):
"""
ACTION:
Sets a row of data in the xml chart_data section.
label is a string name of the data set, followed by any amount
of numeric data items.
Each data object can either be a float/int value, OR, it can be
a dictionary. Providing a dictionary lets you add special effects
and options to each value to be used in the graph display.
The dictionary must AT LEAST have a 'value' key, which is the data
value. Other options are as follows:
label - string label for the individual data
note - string note to be attached to the data in the chart
tooltip - string tooltip message that will appear during mouseOver
shadow - string id of a shadow filter to apply
bevel - string id of a bevel filter to apply
glow - string id of a glow filter to apply
blur - string id of a blur filter to apply
alpha - transparency value from 0-100 (transparent-opaque)
line_color - border line color, string hex value ('000000')
line_alpha - transparency value of border from 0-100 (transparent-opaque)
line_thickness - int pixel thickness of border (default 1 pixel)
For options that take a filter id (shadow, bevel, glow, blur), you must
have defined this filter name using the addFilter_ methods.
Keep in mind that rows must all contain the same number of data
elements to maintain consistency with their column labels.
INPUT:
str label - name of the data set
float/int/dict *data - data1, [data2], [...]
OUTPUT: NONE
"""
if not data:
raise SwfChartException, "Must provide at least one data argument!"
# check row length
if not self._properties['rows']:
self._rowLength = len(data)
else:
if not len(data) == self._rowLength:
raise SwfChartException(
"Number of data element in row must be the same length as the previously added rows!")
# create the row
rowData = ['\t\t']
rowData.append("\t\t\t%s" % label)
for item in data:
if isinstance(item, dict):
valStr = ''
if not item.has_key('value'):
raise SwfChartException, 'data dict argument must at least have a "value" key for the value'
value = item['value']
for k,v in item.iteritems():
if k=='value': continue
valStr += " %s='%s'" % (k,v)
if value != None:
lineStr = "\t\t\t%s" % (valStr, value)
rowData.append(lineStr)
else:
rowData.append("\t\t\t")
# rowData.append("\t\t\t0")
else:
if item == None:
rowData.append("\t\t\t")
# rowData.append("\t\t\t0")
else:
rowData.append("\t\t\t%s" % item)
rowData.append('\t\t')
rowStr = '\n'.join(rowData)
self._properties['rows'] = '\n'.join( (self._properties['rows'], rowStr) )
self._setPropertySet('chart_data')
def clearDrawElements(self):
"""
ACTION: Clear the current draw elements
INPUT: NONE
OUTPUT: NONE
"""
self._properties['drawElements'] = ''
if 'draw' in self._setProperties:
self._setProperties.remove('draw')
def clearFilters(self):
"""
ACTION: Clear the current filters
INPUT: NONE
OUTPUT: NONE
"""
self._properties['filters'] = ''
if 'filter' in self._setProperties:
self._setProperties.remove('filter')
def clearLinks(self):
"""
ACTION: Clear the current links
INPUT: NONE
OUTPUT: NONE
"""
self._properties['links'] = ''
if 'link' in self._setProperties:
self._setProperties.remove('link')
def clearRows(self):
"""
ACTION: Clear the current rows data
INPUT: NONE
OUTPUT: NONE
"""
self._properties['rows'] = ''
if 'chart_data' in self._setProperties:
self._setProperties.remove('chart_data')
def getXML(self):
"""
ACTION:
Return the final formatted XML of all attributes, as a string
Same as treating the class instance as a string.
INPUT: NONE
OUTPUT: NONE
"""
return str(self)
def setLicense(self, lic):
"""
ACTION:
set the license code for the chart, if you have one
INPUT:
str lic - license reg code
OUTPUT : NONE
"""
chartStr = '\t%s' % lic
self._properties['license'] = chartStr
self._setPropertySet('license')
def setChartBorder(self, top=0, bottom=0, left=0, right=0, color='000000'):
"""
ACTION: Set the border thickness and color for the chart
INPUT:
int top - thickness of top
int bottom - thickness of bottom
int left - thickness of left
int right - thickness of right
str color - hex color ('000000', or 'FFFFFF' format)
OUTPUT : NONE
"""
chartStr = '\t'
self._properties['chart_border'] = chartStr
self._setPropertySet('chart_border')
def setChartGrid(self, direction, thickness=1, color='000000', alpha=20, type='solid'):
"""
ACTION: sets the chart's vertical or horizontal grid attributes.
INPUT:
str direction - 'vertical' or 'horizontal' grid lines
int thickness - thickness of grid (default 1)
str color - hex color (default '000000')
alpha - transparency from 0-100 (default 20)
type - line type ('solid', 'dotted', 'dashed')
OUTPUT : NONE
"""
if not direction in ('vertical', 'horizontal'):
raise SwfChartException, "Allowable values for direction arg: ('vertical', 'horizontal')"
else:
d = direction[0]
chartStr = '\t'
self._properties['chart_grid_%s' % d] = chartStr
self._setPropertySet('chart_grid_%s' % d)
def setChartGuide(self, **kwargs):
"""
ACTION:
sets one or two guide lines to connect the cursor position
with the axes and simplify reading their values. The guides
are the two red lines that move with the cursor when the mouse
is over this chart
INPUT:
Valid Key/Value args:
bool horizontal, bool vertical, int thickness, str color ('000000'),
int alpha (0-100), str type ('solid', 'dotted', 'dashed'),
bool snap_h, bool snap_v, bool connect
int radius, str fill_color ('000000'), int fill_alpha (0-100),
str line_color ('000000'), int line_alpha (0-100), int line_thickness
int text_alpha (0-100), int text_v_alpha (0-100), str prefix_h,
str suffix_h, str prefix_v, str suffix_v, int decimals,
str decimal_chars, str separator, str font, bool bold, int size,
str text_color ('000000'), str background_color ('000000')
OUTPUT : NONE
"""
self._setPropertyFromDict('chart_guide', **kwargs)
def setChartLabel(self, **kwargs):
"""
ACTION: sets the attributes of the labels that appear over the graphs
INPUT:
Valid Key/Value args:
str prefix, str suffix, int decimals, str decimal_char,
str separator, str position, bool hide_zero,
bool as_percentage, str font, bool bold, int size,
str color ('000000'), str background_color ('000000'),
int alpha (0-100)
Filter IDs:
str shadow, str bevel, str glow, str blur
OUTPUT : NONE
"""
self._setPropertyFromDict('chart_label', **kwargs)
def setChartNote(self, **kwargs):
"""
ACTION: defines the look of comments that can be attached to data or category points
INPUT:
Valid Key/Value args:
str type, int x, int y, int offset_x, int offset_y,
str font, bool bold, int size, str color ('000000'),
int alpha (0-100), str background_color ('000000'),
int background_alpha (0-100)
Filter IDs:
str shadow, str bevel, str glow, str blur
OUTPUT : NONE
"""
self._setPropertyFromDict('chart_note', **kwargs)
def setChartPref(self, **kwargs):
"""
ACTION:
sets the preferences for some charts
Groups of key/value attributes are only valid for certain charts
INPUT:
general:
bool zero_line
line chart:
int line_thickness, str point_shape, int point_size,
bool fill_shape, bool connect, str tip
candlestick chart:
str type, int line_thickness, str bull_color, str bear_color
scatter chart:
int point_size, str point_shape, int trend_thickness,
int trend_alpha, int line_thickness
3d chart:
int rotation_x, int rotation_y, bool drag, int min_x,
int max_x, int min_y, int max_y
polar chart:
str type, str grid, int line_thickness, str point_shape,
int point_size, bool fill_shape
pie chart:
bool select
image chart:
bool stretch
donut chart:
bool empty_center, bool select
OUTPUT : NONE
"""
self._setPropertyFromDict('chart_pref', **kwargs)
def setChartRect(self, **kwargs):
"""
ACTION:
sets the chart's rectangle. This is the area in which the graphing
is drawn. Not including the legend or the axis labels
INPUT:
Valid Key/Value args:
int x, int y, int width, int height,
str positive_color, str negative_color,
int positive_alpha, int negative_alpha,
int corner_tl, int corner_tr,
int corner_br, int corner_bl,
bool hide
Filter IDs:
str shadow, str bevel, str glow, str blur
OUTPUT : NONE
"""
self._setPropertyFromDict('chart_rect', **kwargs)
def setChartTransition(self, type='none', delay=0, duration=1, order='all'):
"""
ACTION:
sets the chart's transition attributes
INPUT:
str type - (dissolve, drop, spin, scale, zoom, blink, slide_right,
slide_left, slide_up, slide_down, and none)
float delay - delay in seconds (default 0)
float duration - duration in seconds (default 1)
str order - order in which to transition the charts parts
(series, category, all)
OUTPUT : NONE
"""
chartStr = '\t'
self._properties['chart_transition'] = chartStr
self._setPropertySet('chart_transition')
def setChartType(self, *types):
"""
ACTION:
sets the type of chart to draw
You may specify one or more chart types as args. Note that
the number of mixed chart types must correspond to the
number of rows that were added. Each row will be
represented with the matching chart type.
Only area, column, and line chart types are valid for
mixed charts
INPUT:
str type -
column (default)
stacked column
floating column
3d column
image column
stacked 3d column
parallel 3d column
pie
3d pie
image pie
donut
bar
stacked bar
floating bar
area
stacked area
3d area
stacked 3d area
candlestick
scatter
polar
bubble
vline
OUTPUT : NONE
"""
mixedCharts = ('area', 'column', 'line')
chartStr = '\t'
if len(types) == 1:
chartStr += str(types[0])
endStr = ''
else:
endStr = '\n\t'
for c in types:
c = str(c)
if not c in mixedCharts:
raise SwfChartException(
"Chart type '%s' is not valid for mixed charts. Must be %s" % (c, mixedCharts))
chartStr += '\n\t\t%s' % c
chartStr += '%s' % endStr
self._properties['chart_type'] = chartStr
self._setPropertySet('chart_type')
def setSeries(self, bar_gap=0, set_gap=20, transfer=False):
"""
ACTION:
determines the look of the series graphs of some chart types
INPUT:
int bar_gap - 0 to 100
int set_gap - 0 to 100
bool transfer - default False
OUTPUT : NONE
"""
chartStr = '\t'
self._properties['series'] = chartStr
self._setPropertySet('series')
def setSeriesColor(self, *colors):
"""
ACTION:
sets the colors to use for the chart series
Color is a 6 character hex value, ie. '000000'
Each color arg corresponds to the order of rows (series)
in the graph
INPUT:
str color1, [color2], [...]
OUTPUT : NONE
"""
if not colors:
return
chartStr = '\t'
for c in colors:
if len(c) != 6 and not c.isalnum():
raise SwfChartException('Color value "%s" must be a 6 character HEX color' % c)
chartStr += "\n\t\t%s" % c
chartStr += '\n\t'
self._properties['series_color'] = chartStr
self._setPropertySet('series_color')
def setSeriesExplode(self, *vals):
"""
ACTION:
Applies to pie, line, and scatter charts ONLY.
A percentage value determining how far to sepearate
a 'slice' from its center. Each value in the args
corresponds to that 'slice' in the graph.
If you just set one value to 50, then only the first slice
is affected and set to 50% explode, and so on.
INPUT:
int val1, [val2], [...]
OUTPUT : NONE
"""
if not vals:
return
chartStr = '\t'
for v in vals:
chartStr += "\n\t\t%d" % v
chartStr += '\n\t'
self._properties['series_explode'] = chartStr
self._setPropertySet('series_explode')
def setAxisCategory(self, **kwargs):
"""
ACTION: sets the label attributes for the category-axis
INPUT:
Valid Key/Value pairs:
Review API docs
OUTPUT : NONE
"""
self._setPropertyFromDict('axis_category', **kwargs)
def setAxisCategoryLabel(self, *labels):
"""
ACTION:
Overrides axis labels
INPUT:
string label1, [label2], [...]
OUTPUT : NONE
"""
if not labels:
return
chartStr = '\t'
for v in labels:
chartStr += "\n\t\t%s" % v
chartStr += '\n\t'
self._properties['axis_category_label'] = chartStr
self._setPropertySet('axis_category_label')
def setAxisTicks(self, **kwargs):
"""
ACTION: sets the tick marks on the chart axes
INPUT:
Valid Key/Value pairs:
Review API docs
OUTPUT : NONE
"""
self._setPropertyFromDict('axis_ticks', **kwargs)
def setAxisValue(self, **kwargs):
"""
ACTION: sets the label attributes for the value-axis
INPUT:
Valid Key/Value pairs:
Review API docs
OUTPUT : NONE
"""
self._setPropertyFromDict('axis_value', **kwargs)
def setAxisValueLabel(self, *labels):
"""
ACTION:
Overrides axis labels
INPUT:
string label1, [label2], [...]
OUTPUT : NONE
"""
if not labels:
return
chartStr = '\t'
for v in labels:
chartStr += "\n\t\t%s" % v
chartStr += '\n\t'
self._properties['axis_value_label'] = chartStr
self._setPropertySet('axis_value_label')
def setContextMenu(self, about=True, printOpt=True, full_screen=True, quality=True, jpeg_url=None):
"""
ACTION:
sets the options for the right click context menu of the graph
INPUT:
bool about - show the About SWF Charts option
bool printOpt - option to print graph
bool full_screen - show graph in full screen mode
bool quality - Include quality options in context
str jpeg_url - url of a the php script that enables Export to JPEG
(must be server-side)
OUTPUT : NONE
"""
chartStr = '\t'
self._properties['context_menu'] = chartStr
self._setPropertySet('context_menu')
def setLegend(self, **kwargs):
"""
ACTION:
sets the legend's attributes. The legend is the area that
identifies the colors assigned to the graphs
INPUT:
Valid Key/Value pairs:
Review API docs
OUTPUT : NONE
"""
self._setPropertyFromDict('legend', **kwargs)
def setLinkData(self, url, target='_self', timeout=30, retry=2, spinning_wheel=False):
"""
ACTION:
sets the overall link action for clicking on any chart element
INPUT:
str url - relative or absolute URL to process
str target - window to open or action to take
* Valid if target == 'update'
int timeout - time in seconds to wait for update to complete before fail
int retry - how many times to retry the update
bool spinning_wheel - show a spinning wheel while updating
OUTPUT : NONE
"""
chartStr = '\t'
self._properties['link_data'] = chartStr
self._setPropertySet('link_data')
def setScroll(self, **kwargs):
"""
ACTION:
activates and sets the attributes of a slider that makes
the chart scrollable. Scrolling is supported by all chart
types except for 3d, pie, donut, polar, scatter, bubble,
and image charts.
INPUT:
Valid Key/Value pairs:
Review API docs
OUTPUT : NONE
"""
self._setPropertyFromDict('scroll', **kwargs)
def setTooltip(self, **kwargs):
"""
ACTION:
defines the look of the cursor label that appears
when the mouse moves over some chart elements
INPUT:
Valid Key/Value pairs:
Review API docs
OUTPUT : NONE
"""
self._setPropertyFromDict('tooltip', **kwargs)
def setUpdate(self, url, delay=0, timeout=30, retry=2, mode='normal', span=10):
"""
ACTION:
updates the chart without reloading the web page. This makes
it possible to display charts with live data, change the chart's
look over time for emphasis, or create a slideshow from different charts.
INPUT:
str url - relative or absolute url to get XML source
int delay - interval between updates, in seconds
int timeout - time to wait for an update before timing out, in seconds
int retry - number of times to retry before erroring out, default 2
str mode - update mode applied to chart
(normal, reset, data, stream_category, stream_series)
int span - applies only if mode is stream_category or stream_series.
max number of rows or columns to maintain before deleting old
OUTPUT : NONE
"""
modeTypes = ('normal', 'reset', 'data', 'stream_category', 'stream_series')
if not mode in modeTypes:
raise SwfChartException(
"mode '%s' is not one of the allowed types: %s" % (mode, modeTypes))
chartStr = '\t'
self._properties['update'] = chartStr
self._setPropertySet('update')