Anonymous avatar Anonymous committed 4294188 Draft

remove scrumburndown plugin.

Comments (0)

Files changed (31)

scrumburndownplugin/README.txt

-
-Dependencies for testing:
- - Nose (easy_install nose)
- - Nose test config (easy_install nose-testconfig)
- - Coverage (easy_install coverage)
- 
-Running tests
-  You can run tests by executing the following command in the root of the project:
-  
-     python setup.py test
-     
-     
-Running tests with coverage
-	
-    nosetests --cover-erase --with-coverage
-	coverage -r burndown/burndown.py burndown/dbhelper.py
-	coverage -a burndown/burndown.py
-    

scrumburndownplugin/burndown/__init__.py

-from burndown import *
-from burndown_admin_milestones import *

scrumburndownplugin/burndown/burndown.py

-# Burndown plugin
-# Copyright (C) 2006 Sam Bloomquist <spooninator@hotmail.com>
-# Copyright (C) 2006-2008 Daan van Etten <daan@stuq.nl>
-# All rights reserved.
-
-# This software may at some point consist of voluntary contributions made by
-# many individuals. For the exact contribution history, see the revision
-# history and logs, available at http://projects.edgewall.com/trac/.
-#
-# Author: Sam Bloomquist <spooninator@hotmail.com>
-# Author: Daan van Etten <daan@stuq.nl>
-
-import time
-import datetime
-import sys
-
-import dbhelper
-
-from trac import __version__ as tracversion_runtime
-
-from datetime import datetime, date
-
-from trac.core import *
-from trac.config import BoolOption
-from trac.env import IEnvironmentSetupParticipant
-from trac.perm import IPermissionRequestor
-from trac.web.chrome import INavigationContributor, ITemplateProvider, add_stylesheet, add_script
-from trac.web.main import IRequestHandler
-from trac.util import escape, Markup, format_date
-from trac.ticket import ITicketChangeListener
-from trac.ticket import model
-from trac.util.datefmt import to_datetime
-
-class BurndownComponent(Component):
-    implements(IEnvironmentSetupParticipant, INavigationContributor,
-                    IRequestHandler, ITemplateProvider, IPermissionRequestor, ITicketChangeListener)
-
-    tracversion=tracversion_runtime[:4]
-    
-    #---------------------------------------------------------------------------
-    # IEnvironmentSetupParticipant methods
-    #---------------------------------------------------------------------------
-    def environment_created(self):
-        """Called when a new Trac environment is created."""
-        if self.environment_needs_upgrade(None):
-            self.upgrade_environment(None)
-
-    def environment_needs_upgrade(self, db):
-        if not db:
-            db = self.env.get_db_cnx()            
-
-        needsUpgrade = True
-
-        # See if the burndown table exists, if not, we need an upgrade
-        if dbhelper.table_exists(db, "burndown"):
-            needsUpgrade = False
-            if dbhelper.table_field_exists(db, "burndown", "week"):
-                needsUpgrade = True
-
-        if dbhelper.table_field_exists(db, "milestone", "started"):
-            needsUpgrade = False
-
-        return needsUpgrade
-
-    def upgrade_environment(self, db):
-        db = self.env.get_db_cnx()
-        
-        needsCreate = True
-        needsUpgrade_milestone = True
-        needsUpgrade_burndown = False
-        
-        if dbhelper.table_exists(db, "burndown"):
-            needsCreate = False
-            if dbhelper.table_field_exists(db, "burndown", "week"):
-                needsUpgrade_burndown = True
-
-        if dbhelper.table_field_exists(db, "milestone", "started"):
-            needsUpgrade_milestone = False
-        
-        if needsCreate:
-            print >> sys.stderr, 'Attempting to create the burndown table'
-            dbhelper.create_burndown_table(db, self.env)
-
-        if needsUpgrade_milestone:
-            print >> sys.stderr, 'Attempting to modify the milestone table'
-            dbhelper.upgrade_milestone_table(db, self.env)
-            
-        if needsUpgrade_burndown:
-            print >> sys.stderr, 'Attempting to modify the burndown table'
-            dbhelper.upgrade_burndown_table(db, self.env)
-
-        db.commit()
-            
-    #---------------------------------------------------------------------------
-    # ITicketChangeListener methods
-    #---------------------------------------------------------------------------
-    
-    def ticket_created(self, ticket):
-        self.log.debug('burndown plugin - ticket_created')
-        self.update_burndown_data()
-        
-    def ticket_changed(self, ticket, comment, author, old_values):
-        self.log.debug('burndown plugin - ticket_changed')
-        self.update_burndown_data()
-        
-    def ticket_deleted(self, ticket):
-        self.log.debug('burndown plugin - ticket_modified')
-        self.update_burndown_data()
-
-    #---------------------------------------------------------------------------
-    # INavigationContributor methods
-    #---------------------------------------------------------------------------
-    def get_active_navigation_item(self, req):
-        return "burndown"
-
-    def get_navigation_items(self, req):
-        if req.perm.has_permission("BURNDOWN_VIEW"):
-            if self.tracversion=="0.10":
-                yield 'mainnav', 'burndown', Markup('<a href="%s">Burndown</a>') % req.href.burndown()
-            else:
-                yield 'mainnav', 'burndown', Markup('<a href="%s">Burndown</a>' % req.href.burndown())
-
-    #---------------------------------------------------------------------------
-    # IPermissionRequestor methods
-    #---------------------------------------------------------------------------
-    def get_permission_actions(self):
-        return ["BURNDOWN_VIEW", "BURNDOWN_ADMIN"]
-
-    #---------------------------------------------------------------------------
-    # IRequestHandler methods
-    #---------------------------------------------------------------------------
-    def match_request(self, req):
-        return req.path_info == '/burndown'
-    
-    def process_request(self, req):
-        req.perm.assert_permission('BURNDOWN_VIEW')
-
-        db = self.env.get_db_cnx()
-
-        milestones = dbhelper.get_milestones(db)
-        components = dbhelper.get_components(db)
-        
-        selected_milestone = None
-        if (len(milestones)>0):
-            selected_milestone = dbhelper.get_current_milestone(db, req.args.get('selected_milestone', ""))
-        
-        selected_component = req.args.get('selected_component', 'All Components')
-        
-        empty_db_for_testing = req.args.get('empty_db_for_testing', 'false')
-        if empty_db_for_testing == "true":
-            req.perm.assert_permission('TRAC_ADMIN')
-            dbhelper.empty_db_for_testing(db)
-        
-        # expose display data to the templates
-        data = {}
-        data['milestones'] = req.hdf['milestones'] = milestones
-        data['components'] = req.hdf['components'] = components
-        data['selected_milestone'] = req.hdf['selected_milestone'] = selected_milestone
-        data['selected_component'] = req.hdf['selected_component'] = selected_component
-        data['draw_graph'] = req.hdf['draw_graph'] = False
-        data['start'] = req.hdf['start'] = False
-        
-        if req.perm.has_permission("BURNDOWN_ADMIN"):
-            data['start'] = req.hdf['start'] = True # show the start and complete milestone buttons to admins
-        
-        if req.args.has_key('start'):
-            self.start_milestone(db, selected_milestone['name'])
-
-        data['draw_graph'] = req.hdf['draw_graph'] = True
-        self.update_burndown_data()
-                
-        data['burndown_data'] = req.hdf['burndown_data'] = []
-        burndown_data, estimated_data = self.get_burndown_data(db, selected_milestone, components, selected_component)
-        data['burndown_data'] = req.hdf['burndown_data'] = burndown_data
-        data['estimated_data'] = req.hdf['estimated_data'] = estimated_data
-        
-        add_stylesheet(req, 'hw/css/burndown.css')
-        
-        self.update_burndown_data()
-        
-        if self.tracversion=="0.10":
-            add_script(req, 'hw/js/line.js')
-            add_script(req, 'hw/js/wz_jsgraphics.js')
-            return 'burndown.cs', None
-        else:
-            data['library'] = ''
-            if data['library'] == 'flot':
-                add_script(req, 'hw/js/jquery.flot.js')
-            else:
-                add_script(req, 'hw/js/line.js')
-                add_script(req, 'hw/js/wz_jsgraphics.js')
-            
-            return 'burndown.html', data, None
-
-
-    def get_burndown_data(self, db, selected_milestone, components, selected_component):
-        cursor = db.cursor()
-        cursor.execute("SELECT due FROM milestone where name='%s'" % selected_milestone['name']);
-        milestone_due = cursor.fetchone()[0];
-        t = to_datetime(milestone_due)
-        due_milestone = datetime(t.year,t.month,t.day)
-
-        delta = None
-        component_data = {} # this will be a dictionary of lists of tuples -- e.g. component_data = {'componentName':[(id, hours_remaining), (id, hours_remaining), (id, hours_remaining)]}
-        for comp in components:
-            if selected_component == 'All Components' or comp['name'] == selected_component:
-                sqlBurndown = "SELECT id, hours_remaining ,date "\
-                                    "FROM burndown "\
-                                    "WHERE milestone_name = '%s' AND component_name = '%s' "\
-                                    "ORDER BY id" % (selected_milestone['name'], comp['name'])
-                cursor.execute(sqlBurndown)
-                component_data[comp['name']] = cursor.fetchall()
-                self.log.debug(component_data[comp['name']])
-
-        if len(components)==0:
-            comp = {'name':'-'} 
-            if selected_component == 'All Components' or comp['name'] == selected_component:
-                self.log.debug("comp = %s", comp['name'])
-                self.log.debug("selected_component = %s", selected_component)
-                sqlBurndown = "SELECT id, hours_remaining, date "\
-                                    "FROM burndown "\
-                                    "WHERE milestone_name = %s AND component_name = %s "\
-                                    "ORDER BY id"
-                cursor.execute(sqlBurndown, (selected_milestone['name'], comp['name']))
-                component_data[comp['name']] = cursor.fetchall()
-                self.log.debug(component_data[comp['name']])
-            
-        if len(component_data) > 0 and component_data[component_data.keys()[0]]:
-            burndown_length = len(component_data[component_data.keys()[0]])
-        else:
-            burndown_length = 0
-        burndown_data = []
-        plotdata = []
-        if selected_component == 'All Components':
-            for time_unit in range (0, burndown_length):
-                sumHours = 0
-                for comp in components:
-                    plotdata.append(component_data[comp['name']][time_unit][2])
-                    self.log.debug('component: %s', [comp['name']]);
-                    self.log.debug('time_unit: %s', [time_unit]);
-                    if (component_data[comp['name']] and len(component_data[comp['name']]) > time_unit):
-                        self.log.debug('hours: %s', component_data[comp['name']][time_unit][1]);
-                        sumHours += component_data[comp['name']][time_unit][1]
-                if len(components)==0:
-                    comp={'name':'-'}
-                    self.log.debug('component: %s', [comp['name']]);
-                    self.log.debug('time_unit: %s', [time_unit]);
-                    if (component_data[comp['name']] and len(component_data[comp['name']]) > time_unit):
-                        self.log.debug('hours: %s', component_data[comp['name']][time_unit][1]);
-                        sumHours += component_data[comp['name']][time_unit][1]
-                
-                burndown_data.append((time_unit+1, sumHours))
-                
-        else:
-            for time_unit in range (0, len(component_data[selected_component])):
-                plotdata.append(component_data[selected_component]['date'])
-                burndown_data.append((time_unit+1, component_data[selected_component][time_unit][1]))
-        start_milestone = None
-        estimated_data = []
-        if len(burndown_data)!=0:
-            if len(components)==0:
-                t = time.strptime(component_data['-'][0][2],'%Y/%m/%d')
-                start_milestone = datetime(t[0] ,t[1] ,t[2])
-            else:
-                t = time.strptime(component_data[component_data.keys()[0]][0][2],'%Y/%m/%d')
-                start_milestone = datetime(t[0] ,t[1] ,t[2])
-            initial_estimated = burndown_data[0][1]
-
-            term = (due_milestone - start_milestone).days + 1
-
-            for time_unit in range (0,term):
-                estimated_data.append((time_unit+1 ,initial_estimated*(1-float(time_unit+1)/term) ))
-   
-        return (burndown_data, estimated_data)
-        
-    def start_milestone(self, db, milestone):
-        startdate = dbhelper.get_startdate_for_milestone(db, milestone)
-        
-        if startdate != None:
-            raise TracError("Milestone %s was already started." % milestone)
-
-        dbhelper.set_startdate_for_milestone(db, milestone, int(time.time()))
-        
-    #---------------------------------------------------------------------------
-    # ITemplateProvider methods
-    #---------------------------------------------------------------------------
-    def get_templates_dirs(self):
-        from pkg_resources import resource_filename
-        return [resource_filename(__name__, 'templates')]
-
-    def get_htdocs_dirs(self):
-        from pkg_resources import resource_filename
-        return [('hw', resource_filename(__name__, 'htdocs'))]
-
-    #------------------------------------------------------------------------
-    # update_burndown_data
-    #  - add up the hours remaining for the open tickets for each open milestone and put the sums into the burndown table
-    #------------------------------------------------------------------------
-    def update_burndown_data(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        
-        # today's date
-        today = format_date(int(time.time()))
-
-        milestones = dbhelper.get_milestones(db)
-        components = dbhelper.get_components(db)
-        for mile in milestones:
-            if mile['started'] and not mile['completed']: # milestone started, but not completed
-                for comp in components:
-                    sqlSelect =     "SELECT est.value AS estimate, ts.value AS spent "\
-                                        "FROM ticket t "\
-                                        "    LEFT OUTER JOIN ticket_custom est ON (t.id = est.ticket AND est.name = 'estimatedhours') "\
-                                        "    LEFT OUTER JOIN ticket_custom ts ON (t.id = ts.ticket AND ts.name = 'totalhours') "\
-                                        "WHERE t.component = %s AND t.milestone = %s"\
-                                        "    AND status IN ('new', 'assigned', 'reopened', 'accepted') "
-                    cursor.execute(sqlSelect, [comp['name'], mile['name']])
-                
-                    rows = cursor.fetchall()
-                    hours = 0
-                    estimate = 0
-                    spent = 0
-                    if rows:
-                        for estimate, spent in rows:
-                            if not estimate:
-                                estimate = 0
-                            if not spent:
-                                spent = 0
-                        
-                            if (float(estimate) - float(spent)) > 0:
-                                hours += float(estimate) - float(spent)
-
-                    cursor.execute("SELECT id FROM burndown WHERE date = %s AND milestone_name = %s"\
-                                        "AND component_name = %s", [today, mile['name'], comp['name']])
-            
-                    row = cursor.fetchone()
-                    
-                    try:
-                        if row:
-                            cursor.execute("UPDATE burndown SET hours_remaining = %s WHERE date = %s AND milestone_name = %s"\
-                                           "AND component_name = %s", [hours, today, mile['name'], comp['name']])
-                        else:
-                            cursor.execute("INSERT INTO burndown(component_name, milestone_name, date, hours_remaining) "\
-                                           "    VALUES(%s,%s,%s,%s)", [comp['name'], mile['name'], today, hours])
-                    except Exception, inst:
-                        self.log.debug(type(inst))     # the exception instance
-                        self.log.debug(inst.args)      # arguments stored in .args
-                        self.log.debug(inst)           # __str__ allows args to printed directly
-                        cursor.connection.rollback()
-                    else:
-                        db.commit()
-                if len(components)==0:
-                    comp = {'name':'-'}
-                    sqlSelect =     "SELECT est.value AS estimate, ts.value AS spent "\
-                                        "FROM ticket t "\
-                                        "    LEFT OUTER JOIN ticket_custom est ON (t.id = est.ticket AND est.name = 'estimatedhours') "\
-                                        "    LEFT OUTER JOIN ticket_custom ts ON (t.id = ts.ticket AND ts.name = 'totalhours') "\
-                                        "WHERE t.milestone = %s"\
-                                        "    AND status IN ('new', 'assigned', 'reopened', 'accepted') "
-                    cursor.execute(sqlSelect, [mile['name']])
-                
-                    rows = cursor.fetchall()
-                    hours = 0
-                    estimate = 0
-                    spent = 0
-                    if rows:
-                        for estimate, spent in rows:
-                            if not estimate:
-                                estimate = 0
-                            if not spent:
-                                spent = 0
-                        
-                            if (float(estimate) - float(spent)) > 0:
-                                hours += float(estimate) - float(spent)
-
-                    cursor.execute("SELECT id FROM burndown WHERE date = %s AND milestone_name = %s"\
-                                        "AND component_name = %s", [today, mile['name'], comp['name']])
-            
-                    row = cursor.fetchone()
-                    
-                    try:
-                        if row:
-                            cursor.execute("UPDATE burndown SET hours_remaining = %s WHERE date = %s AND milestone_name = %s"\
-                                           "AND component_name = %s", [hours, today, mile['name'], comp['name']])
-                        else:
-                            cursor.execute("INSERT INTO burndown(component_name, milestone_name, date, hours_remaining) "\
-                                           "    VALUES(%s,%s,%s,%s)", [comp['name'], mile['name'], today, hours])
-                    except Exception, inst:
-                        self.log.debug(type(inst))     # the exception instance
-                        self.log.debug(inst.args)      # arguments stored in .args
-                        self.log.debug(inst)           # __str__ allows args to printed directly
-                        cursor.connection.rollback()
-                    else:
-                        db.commit()
-                                         
-        

scrumburndownplugin/burndown/burndown_admin_milestones.py

-# -*- coding: utf-8 -*-
-from trac.core import *
-import dbhelper
-
-try: # Trac 0.11 only
-   from trac.admin import IAdminPanelProvider
-   from trac.util.datefmt import utc, parse_date, get_date_format_hint, get_datetime_format_hint
-   from trac.util.datefmt import to_timestamp
-except ImportError:
-    IAdminPanelProvider = None
-
-
-
-class BurndownMilestonesAdminPanel(Component):
-    if IAdminPanelProvider:
-        implements(IAdminPanelProvider)
-    
-    #---------------------------------------------------------------------------
-    # IAdminPanelProvider methods
-    #---------------------------------------------------------------------------
-    def get_admin_panels(self, req):
-        if req.perm.has_permission('BURNDOWN_ADMIN'):
-            yield ('burndown', u'スクラムバーンダウンチャート', 'settings', u'設定')
-
-    def render_admin_panel(self, req, cat, page, milestone):
-        req.perm.assert_permission('BURNDOWN_ADMIN')
-        db = self.env.get_db_cnx()            
-
-        if milestone:
-            mil = dbhelper.get_milestone(db, milestone)
-            if req.method == 'POST':
-                if req.args.get('save'):
-                    mil['started'] = None
-                    started = req.args.get('started', '')
-                    if started:
-                        startdate = parse_date(started, req.tz)
-                        # todo: fill empty dates in
-                        mil['started'] = startdate
-                        dbhelper.set_startdate_for_milestone(db, mil['name'], to_timestamp(mil['started']))
-
-                    req.redirect(req.href.admin(cat, page))
-                elif req.args.get('cancel'):
-                    req.redirect(req.href.admin(cat, page))
-            
-            data = {'view': 'detail', 
-                    'milestone': mil
-                    }
-        else:
-            db = self.env.get_db_cnx()
-            cursor = db.cursor()
-            milestones = []
-            for milestone in dbhelper.get_milestones(db):
-                milestones.append(milestone)
-
-            data = {'view': 'list',
-                    'milestones': milestones,
-                    'default': self.config.get('ticket', 'default_milestone')
-                    }
-            
-        data.update({
-            'date_hint': get_date_format_hint(),
-            'datetime_hint': get_datetime_format_hint()
-        })
-
-        return 'config.html', data

scrumburndownplugin/burndown/dbhelper.py

-from datetime import datetime
-import time
-
-
-from trac.core import ComponentManager 
-from trac.db import Table, Column, Index, DatabaseManager
-
-burndown_table = Table('burndown', key=('id'))[Column('id', auto_increment=True),
-                                             Column('component_name'), 
-                                             Column('milestone_name'), 
-                                             Column('date'), 
-                                             Column('hours_remaining', type='int'), 
-                                             Index(['id'])]
-                            
-milestone_table = Table('milestone', key='name')[Column('name'),
-                                                 Column('due', type='int'),
-                                                 Column('completed', type='int'),
-                                                 Column('description'),
-                                                 Column('started', type='int')]
-
-def get_current_milestone(db, milestone_name):
-    cursor = db.cursor()
-    mile = None
-    
-    if milestone_name != None:
-        mile = get_milestone(db, milestone_name)
-        
-    if not mile:
-        cursor.execute("select name from milestone order by started desc, name limit 1")
-        milestone = cursor.fetchone()
-        if milestone:
-            mile = get_milestone(db, milestone[0])
-        
-    return mile
-
-def empty_db_for_testing(db):
-    cursor = db.cursor()
-    cursor.execute("delete from ticket_custom;")
-    cursor.execute("delete from ticket;")
-    cursor.execute("delete from burndown;")    
-    cursor.execute("update milestone set started=0, completed=0, due=0;")    
-    cursor.execute("update milestone set due=%s where name='milestone1';", [time.time() + 3600*24*7])    
-    db.commit()
-    
-def get_milestone(db, milestone):
-    cursor = db.cursor()
-    cursor.execute("SELECT name, due, completed, started, description FROM milestone WHERE name = %s", [milestone])
-    mile = cursor.fetchone()
-    if mile != None:
-        return {'name': mile[0], 'due': mile[1], 'completed': mile[2], 'started': mile[3], 'description': mile[4]}
-    else:
-        return None
-            
-def get_milestones(db):
-    cursor = db.cursor()
-    cursor.execute("SELECT name, due, completed, started, description FROM milestone order by name")
-    milestone_lists = cursor.fetchall()
-    milestones = []
-    for mile in milestone_lists:
-        milestones.append({'name': mile[0], 'due': mile[1], 'completed': mile[2], 'started': mile[3], 'description': mile[4]})
-    return milestones
-            
-def get_components(db):
-    cursor = db.cursor()
-    cursor.execute("SELECT name, owner, description FROM component")
-    component_lists = cursor.fetchall()
-    components = []
-    for comp in component_lists:
-        components.append({'name': comp[0], 'owner': comp[1], 'description': comp[2]})
-    return components
-        
-def table_exists(db, table_name):
-    cursor = db.cursor()
-    try:
-        cursor.execute("SELECT * FROM %s" % table_name)
-    except:
-        cursor.connection.rollback()
-        return False
-    return True
-        
-def table_field_exists(db, table_name, field_name):
-    cursor = db.cursor()
-    try:
-        cursor.execute("SELECT %s FROM %s" % (field_name, table_name))
-    except:
-        cursor.connection.rollback()
-        return False
-    return True
-        
-def get_startdate_for_milestone(db, milestone):
-    cursor = db.cursor()
-    cursor.execute("SELECT started FROM milestone WHERE name = %s", [milestone])
-    row = cursor.fetchone()
-    
-    try:
-        if(row and row[0]!=0):
-            return datetime.fromtimestamp(row[0])
-    except TypeError:
-        pass
-    return None
-
-def set_startdate_for_milestone(db, milestone, startdate):
-    cursor = db.cursor()
-    try:
-        cursor.execute("UPDATE milestone SET started = %s WHERE name = %s", [startdate, milestone])
-    except Exception, e:
-        print "Error while updating milestone start date, %s" % (e)
-        db.rollback();
-        return
-    db.commit()
-
-def create_burndown_table(db, env):
-    cursor = db.cursor()
-    
-    db_backend, _ = DatabaseManager(env)._get_connector()
-    for stmt in db_backend.to_sql(burndown_table):
-        try:
-            cursor.execute(stmt)
-        except Exception, e:
-            print "Upgrade failed\nSQL:\n%s\nError message: %s" % (stmt, e)
-            db.rollback();
-            return
-    db.commit()
-    
-def upgrade_burndown_table(db, env):
-    cursor = db.cursor()
-
-    try:
-        cursor.execute("CREATE TEMPORARY TABLE burndown_old as SELECT * FROM burndown;")
-        cursor.execute("DROP TABLE burndown;")
-    
-        db_backend, _ = DatabaseManager(env)._get_connector()
-        for stmt in db_backend.to_sql(burndown_table):
-            cursor.execute(stmt)
-    
-        cursor.execute("""INSERT INTO burndown (id, component_name, milestone_name, date, hours_remaining)
-                          SELECT id, component_name, milestone_name, date, hours_remaining FROM burndown_old;""")
-
-        db.commit()
-    except Exception, e:
-        print "Upgrade of the Burndown plugin failed\nError message: %s" % (e)
-        db.rollback();
-        return
-    
-
-def upgrade_milestone_table(db, env):
-    cursor = db.cursor()
-
-    try:
-        cursor.execute("CREATE TEMPORARY TABLE milestone_old as SELECT * FROM milestone;")
-        cursor.execute("DROP TABLE milestone;")
-    
-        db_backend, _ = DatabaseManager(env)._get_connector()
-        for stmt in db_backend.to_sql(milestone_table):
-            cursor.execute(stmt)
-    
-        cursor.execute("""INSERT INTO milestone(name, due, completed, started, description)
-                          SELECT name, due, completed, 0, description FROM milestone_old;""")
-        
-        db.commit()
-    except Exception, e:
-        print "Upgrade of the Burndown plugin failed\nError message: %s" % (e)
-        db.rollback();
-        return
-

scrumburndownplugin/burndown/htdocs/css/burndown.css

-
-.burndown td, .burndown td.active { padding: 0.2em 0; }
-
-.burndown td a { width: 100%; display: block; border: 0; }
-
-.burndown a.color1 { background: #fdc; color: #a22;
-	border: 1px solid #e88;  border-bottom: 5px solid #e88; }
-.burndown a.color2 { background: #ffb; color: #880;
-	border: 1px solid #eea;  border-bottom: 5px solid #eea; }
-.burndown a.color3 { background: #fbfbfb; color: #444; 
-	border: 1px solid #ddd;  border-bottom: 5px solid #ddd; }
-.burndown a.color4 { background: #e7ffff; color: #099;
-	border: 1px solid #cee;  border-bottom: 5px solid #cee; }
-.burndown a.color5 { background: #e7eeff; color: #469;
-	border: 1px solid #cde;  border-bottom: 5px solid #cde; }
-.burndown a.color6 { background: #f0f0f0; color: #888;
-	border: 1px solid #ddd;  border-bottom: 5px solid #ddd; }
-
-.burndown a.color1:hover { background: #fdc; color: #a22 }
-.burndown a.color2:hover { background: #ffb; color: #880 }
-.burndown a.color3:hover { background: #fbfbfb; color: #444 }
-.burndown a.color4:hover { background: #e7ffff; color: #099 }
-.burndown a.color5:hover { background: #e7eeff; color: #469 }
-.burndown a.color6:hover { background: #f0f0f0; color: #888 }
-
Add a comment to this file

scrumburndownplugin/burndown/htdocs/images/trac-scrum-burndown-plugin-logo.png

Removed
Old image

scrumburndownplugin/burndown/htdocs/js/bar.html

-<head>
-<title>Balamurugan S, Java, Apache, Perl, CSS, DHTML, chart, graph, vertical bar, bar, pie, Free Resource, Spiritual, OM, Travelouge, Enlightenment </title>
-<link rel="stylesheet" type="text/css" href="style_ie.css">
-</head>
-
-
-<body>
-
-<body>
-<center>
-<div class="container">
-
-<div class="application">
-
-<div class="bold_font">DHTML / CSS based bar graph</div>
-
-<div style="display:none;">CSS based vertical bar graph</div>
-
-<div class="norm_font">
- This vertical bar graph is done completely using DHTML and CSS! Unlike an applet based solution, this loads fast and blends in with the rest of the page. You can even use a transparent background for it!
-<br><br>
-This package is free software. It is distributed under GPL - legalese removed, it means that you can use this for any purpose, but cannot charge for this software. Any enhancements you make to this piece of code, should be made available free to the general public! You should also carry a link to this page for users to download the latest version.
-
-<br><br>
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
-<br><br>
-Download the <a href="js/graph.js">javascript</a> code and <a href="js/wz_jsgraphics.js"> support library </a>. 
-
-<br>Use <i>View Source</i> for an example.
-</div>
-<hr>
-
-
-<!-- graph code begins here-->
-<script type="text/javascript" src="js/wz_jsgraphics.js"></script>
-<script type="text/javascript" src="js/graph.js"></script>
-
-<div id="myCanvas" style="overflow: auto; position:relative;height:300px;width:400px;"></div>
-
-<script>
-var g = new graph();
-g.add('01<br>Jan', 145);
-g.add('2', 0);
-g.add('3', 50);
-g.add('4', 130);
-g.add('5', 117);
-g.add('6', 175);
-g.add('7', 205);
-g.add('8', 125);
-g.add('9', 125);
-g.add('10', 135);
-g.add('11', 125);
-
-//If called without a height parameter, defaults to 250
-//g.render("myCanvas", "test graph");
-
-g.render("myCanvas", "test graph", 250);
-
-</script>
-<!-- graph code ends here-->
-
-<hr>
-<div class="bold_font"> Links</div>
-<a href="pie.html"> DHTML/CSS Pie Chart </a> <br>
-<a href="line.html"> DHTML/CSS Line Chart </a> <br>
-<a href="index.html"> Home </a> 
-
-<hr>
-<div class=norm_font align=center> 
-<script src="http://www.jexp.com/cgi-bin/counter/counter.pl"></script>
-Visitors since Dec 2005 
-<div style="display:none">Links by <a href="http://www.jexp.com">www.jexp.com</a></div>
-</div>
-
-</body>

scrumburndownplugin/burndown/htdocs/js/excanvas.pack.js

-if(!window.CanvasRenderingContext2D){(function(){var m=Math;var mr=m.round;var ms=m.sin;var mc=m.cos;var Z=10;var Z2=Z/2;var G_vmlCanvasManager_={init:function(opt_doc){var doc=opt_doc||document;if(/MSIE/.test(navigator.userAgent)&&!window.opera){var self=this;doc.attachEvent("onreadystatechange",function(){self.init_(doc)})}},init_:function(doc){if(doc.readyState=="complete"){if(!doc.namespaces["g_vml_"]){doc.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml")}var ss=doc.createStyleSheet();ss.cssText="canvas{display:inline-block;overflow:hidden;"+"text-align:left;width:300px;height:150px}"+"g_vml_\\:*{behavior:url(#default#VML)}";var els=doc.getElementsByTagName("canvas");for(var i=0;i<els.length;i++){if(!els[i].getContext){this.initElement(els[i])}}}},fixElement_:function(el){var outerHTML=el.outerHTML;var newEl=el.ownerDocument.createElement(outerHTML);if(outerHTML.slice(-2)!="/>"){var tagName="/"+el.tagName;var ns;while((ns=el.nextSibling)&&ns.tagName!=tagName){ns.removeNode()}if(ns){ns.removeNode()}}el.parentNode.replaceChild(newEl,el);return newEl},initElement:function(el){el=this.fixElement_(el);el.getContext=function(){if(this.context_){return this.context_}return this.context_=new CanvasRenderingContext2D_(this)};el.attachEvent('onpropertychange',onPropertyChange);el.attachEvent('onresize',onResize);var attrs=el.attributes;if(attrs.width&&attrs.width.specified){el.style.width=attrs.width.nodeValue+"px"}else{el.width=el.clientWidth}if(attrs.height&&attrs.height.specified){el.style.height=attrs.height.nodeValue+"px"}else{el.height=el.clientHeight}return el}};function onPropertyChange(e){var el=e.srcElement;switch(e.propertyName){case'width':el.style.width=el.attributes.width.nodeValue+"px";el.getContext().clearRect();break;case'height':el.style.height=el.attributes.height.nodeValue+"px";el.getContext().clearRect();break}}function onResize(e){var el=e.srcElement;if(el.firstChild){el.firstChild.style.width=el.clientWidth+'px';el.firstChild.style.height=el.clientHeight+'px'}}G_vmlCanvasManager_.init();var dec2hex=[];for(var i=0;i<16;i++){for(var j=0;j<16;j++){dec2hex[i*16+j]=i.toString(16)+j.toString(16)}}function createMatrixIdentity(){return[[1,0,0],[0,1,0],[0,0,1]]}function matrixMultiply(m1,m2){var result=createMatrixIdentity();for(var x=0;x<3;x++){for(var y=0;y<3;y++){var sum=0;for(var z=0;z<3;z++){sum+=m1[x][z]*m2[z][y]}result[x][y]=sum}}return result}function copyState(o1,o2){o2.fillStyle=o1.fillStyle;o2.lineCap=o1.lineCap;o2.lineJoin=o1.lineJoin;o2.lineWidth=o1.lineWidth;o2.miterLimit=o1.miterLimit;o2.shadowBlur=o1.shadowBlur;o2.shadowColor=o1.shadowColor;o2.shadowOffsetX=o1.shadowOffsetX;o2.shadowOffsetY=o1.shadowOffsetY;o2.strokeStyle=o1.strokeStyle;o2.arcScaleX_=o1.arcScaleX_;o2.arcScaleY_=o1.arcScaleY_}function processStyle(styleString){var str,alpha=1;styleString=String(styleString);if(styleString.substring(0,3)=="rgb"){var start=styleString.indexOf("(",3);var end=styleString.indexOf(")",start+1);var guts=styleString.substring(start+1,end).split(",");str="#";for(var i=0;i<3;i++){str+=dec2hex[Number(guts[i])]}if((guts.length==4)&&(styleString.substr(3,1)=="a")){alpha=guts[3]}}else{str=styleString}return[str,alpha]}function processLineCap(lineCap){switch(lineCap){case"butt":return"flat";case"round":return"round";case"square":default:return"square"}}function CanvasRenderingContext2D_(surfaceElement){this.m_=createMatrixIdentity();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=Z*1;this.globalAlpha=1;this.canvas=surfaceElement;var el=surfaceElement.ownerDocument.createElement('div');el.style.width=surfaceElement.clientWidth+'px';el.style.height=surfaceElement.clientHeight+'px';el.style.overflow='hidden';el.style.position='absolute';surfaceElement.appendChild(el);this.element_=el;this.arcScaleX_=1;this.arcScaleY_=1}var contextPrototype=CanvasRenderingContext2D_.prototype;contextPrototype.clearRect=function(){this.element_.innerHTML="";this.currentPath_=[]};contextPrototype.beginPath=function(){this.currentPath_=[]};contextPrototype.moveTo=function(aX,aY){this.currentPath_.push({type:"moveTo",x:aX,y:aY});this.currentX_=aX;this.currentY_=aY};contextPrototype.lineTo=function(aX,aY){this.currentPath_.push({type:"lineTo",x:aX,y:aY});this.currentX_=aX;this.currentY_=aY};contextPrototype.bezierCurveTo=function(aCP1x,aCP1y,aCP2x,aCP2y,aX,aY){this.currentPath_.push({type:"bezierCurveTo",cp1x:aCP1x,cp1y:aCP1y,cp2x:aCP2x,cp2y:aCP2y,x:aX,y:aY});this.currentX_=aX;this.currentY_=aY};contextPrototype.quadraticCurveTo=function(aCPx,aCPy,aX,aY){var cp1x=this.currentX_+2.0/3.0*(aCPx-this.currentX_);var cp1y=this.currentY_+2.0/3.0*(aCPy-this.currentY_);var cp2x=cp1x+(aX-this.currentX_)/3.0;var cp2y=cp1y+(aY-this.currentY_)/3.0;this.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,aX,aY)};contextPrototype.arc=function(aX,aY,aRadius,aStartAngle,aEndAngle,aClockwise){aRadius*=Z;var arcType=aClockwise?"at":"wa";var xStart=aX+(mc(aStartAngle)*aRadius)-Z2;var yStart=aY+(ms(aStartAngle)*aRadius)-Z2;var xEnd=aX+(mc(aEndAngle)*aRadius)-Z2;var yEnd=aY+(ms(aEndAngle)*aRadius)-Z2;if(xStart==xEnd&&!aClockwise){xStart+=0.125}this.currentPath_.push({type:arcType,x:aX,y:aY,radius:aRadius,xStart:xStart,yStart:yStart,xEnd:xEnd,yEnd:yEnd})};contextPrototype.rect=function(aX,aY,aWidth,aHeight){this.moveTo(aX,aY);this.lineTo(aX+aWidth,aY);this.lineTo(aX+aWidth,aY+aHeight);this.lineTo(aX,aY+aHeight);this.closePath()};contextPrototype.strokeRect=function(aX,aY,aWidth,aHeight){this.beginPath();this.moveTo(aX,aY);this.lineTo(aX+aWidth,aY);this.lineTo(aX+aWidth,aY+aHeight);this.lineTo(aX,aY+aHeight);this.closePath();this.stroke()};contextPrototype.fillRect=function(aX,aY,aWidth,aHeight){this.beginPath();this.moveTo(aX,aY);this.lineTo(aX+aWidth,aY);this.lineTo(aX+aWidth,aY+aHeight);this.lineTo(aX,aY+aHeight);this.closePath();this.fill()};contextPrototype.createLinearGradient=function(aX0,aY0,aX1,aY1){var gradient=new CanvasGradient_("gradient");return gradient};contextPrototype.createRadialGradient=function(aX0,aY0,aR0,aX1,aY1,aR1){var gradient=new CanvasGradient_("gradientradial");gradient.radius1_=aR0;gradient.radius2_=aR1;gradient.focus_.x=aX0;gradient.focus_.y=aY0;return gradient};contextPrototype.drawImage=function(image,var_args){var dx,dy,dw,dh,sx,sy,sw,sh;var oldRuntimeWidth=image.runtimeStyle.width;var oldRuntimeHeight=image.runtimeStyle.height;image.runtimeStyle.width='auto';image.runtimeStyle.height='auto';var w=image.width;var h=image.height;image.runtimeStyle.width=oldRuntimeWidth;image.runtimeStyle.height=oldRuntimeHeight;if(arguments.length==3){dx=arguments[1];dy=arguments[2];sx=sy=0;sw=dw=w;sh=dh=h}else if(arguments.length==5){dx=arguments[1];dy=arguments[2];dw=arguments[3];dh=arguments[4];sx=sy=0;sw=w;sh=h}else if(arguments.length==9){sx=arguments[1];sy=arguments[2];sw=arguments[3];sh=arguments[4];dx=arguments[5];dy=arguments[6];dw=arguments[7];dh=arguments[8]}else{throw"Invalid number of arguments";}var d=this.getCoords_(dx,dy);var w2=sw/2;var h2=sh/2;var vmlStr=[];var W=10;var H=10;vmlStr.push(' <g_vml_:group',' coordsize="',Z*W,',',Z*H,'"',' coordorigin="0,0"',' style="width:',W,';height:',H,';position:absolute;');if(this.m_[0][0]!=1||this.m_[0][1]){var filter=[];filter.push("M11='",this.m_[0][0],"',","M12='",this.m_[1][0],"',","M21='",this.m_[0][1],"',","M22='",this.m_[1][1],"',","Dx='",mr(d.x/Z),"',","Dy='",mr(d.y/Z),"'");var max=d;var c2=this.getCoords_(dx+dw,dy);var c3=this.getCoords_(dx,dy+dh);var c4=this.getCoords_(dx+dw,dy+dh);max.x=Math.max(max.x,c2.x,c3.x,c4.x);max.y=Math.max(max.y,c2.y,c3.y,c4.y);vmlStr.push("padding:0 ",mr(max.x/Z),"px ",mr(max.y/Z),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",filter.join(""),", sizingmethod='clip');")}else{vmlStr.push("top:",mr(d.y/Z),"px;left:",mr(d.x/Z),"px;")}vmlStr.push(' ">','<g_vml_:image src="',image.src,'"',' style="width:',Z*dw,';',' height:',Z*dh,';"',' cropleft="',sx/w,'"',' croptop="',sy/h,'"',' cropright="',(w-sx-sw)/w,'"',' cropbottom="',(h-sy-sh)/h,'"',' />','</g_vml_:group>');this.element_.insertAdjacentHTML("BeforeEnd",vmlStr.join(""))};contextPrototype.stroke=function(aFill){var lineStr=[];var lineOpen=false;var a=processStyle(aFill?this.fillStyle:this.strokeStyle);var color=a[0];var opacity=a[1]*this.globalAlpha;var W=10;var H=10;lineStr.push('<g_vml_:shape',' fillcolor="',color,'"',' filled="',Boolean(aFill),'"',' style="position:absolute;width:',W,';height:',H,';"',' coordorigin="0 0" coordsize="',Z*W,' ',Z*H,'"',' stroked="',!aFill,'"',' strokeweight="',this.lineWidth,'"',' strokecolor="',color,'"',' path="');var newSeq=false;var min={x:null,y:null};var max={x:null,y:null};for(var i=0;i<this.currentPath_.length;i++){var p=this.currentPath_[i];if(p.type=="moveTo"){lineStr.push(" m ");var c=this.getCoords_(p.x,p.y);lineStr.push(mr(c.x),",",mr(c.y))}else if(p.type=="lineTo"){lineStr.push(" l ");var c=this.getCoords_(p.x,p.y);lineStr.push(mr(c.x),",",mr(c.y))}else if(p.type=="close"){lineStr.push(" x ")}else if(p.type=="bezierCurveTo"){lineStr.push(" c ");var c=this.getCoords_(p.x,p.y);var c1=this.getCoords_(p.cp1x,p.cp1y);var c2=this.getCoords_(p.cp2x,p.cp2y);lineStr.push(mr(c1.x),",",mr(c1.y),",",mr(c2.x),",",mr(c2.y),",",mr(c.x),",",mr(c.y))}else if(p.type=="at"||p.type=="wa"){lineStr.push(" ",p.type," ");var c=this.getCoords_(p.x,p.y);var cStart=this.getCoords_(p.xStart,p.yStart);var cEnd=this.getCoords_(p.xEnd,p.yEnd);lineStr.push(mr(c.x-this.arcScaleX_*p.radius),",",mr(c.y-this.arcScaleY_*p.radius)," ",mr(c.x+this.arcScaleX_*p.radius),",",mr(c.y+this.arcScaleY_*p.radius)," ",mr(cStart.x),",",mr(cStart.y)," ",mr(cEnd.x),",",mr(cEnd.y))}if(c){if(min.x==null||c.x<min.x){min.x=c.x}if(max.x==null||c.x>max.x){max.x=c.x}if(min.y==null||c.y<min.y){min.y=c.y}if(max.y==null||c.y>max.y){max.y=c.y}}}lineStr.push(' ">');if(typeof this.fillStyle=="object"){var focus={x:"50%",y:"50%"};var width=(max.x-min.x);var height=(max.y-min.y);var dimension=(width>height)?width:height;focus.x=mr((this.fillStyle.focus_.x/width)*100+50)+"%";focus.y=mr((this.fillStyle.focus_.y/height)*100+50)+"%";var colors=[];if(this.fillStyle.type_=="gradientradial"){var inside=(this.fillStyle.radius1_/dimension*100);var expansion=(this.fillStyle.radius2_/dimension*100)-inside}else{var inside=0;var expansion=100}var insidecolor={offset:null,color:null};var outsidecolor={offset:null,color:null};this.fillStyle.colors_.sort(function(cs1,cs2){return cs1.offset-cs2.offset});for(var i=0;i<this.fillStyle.colors_.length;i++){var fs=this.fillStyle.colors_[i];colors.push((fs.offset*expansion)+inside,"% ",fs.color,",");if(fs.offset>insidecolor.offset||insidecolor.offset==null){insidecolor.offset=fs.offset;insidecolor.color=fs.color}if(fs.offset<outsidecolor.offset||outsidecolor.offset==null){outsidecolor.offset=fs.offset;outsidecolor.color=fs.color}}colors.pop();lineStr.push('<g_vml_:fill',' color="',outsidecolor.color,'"',' color2="',insidecolor.color,'"',' type="',this.fillStyle.type_,'"',' focusposition="',focus.x,', ',focus.y,'"',' colors="',colors.join(""),'"',' opacity="',opacity,'" />')}else if(aFill){lineStr.push('<g_vml_:fill color="',color,'" opacity="',opacity,'" />')}else{lineStr.push('<g_vml_:stroke',' opacity="',opacity,'"',' joinstyle="',this.lineJoin,'"',' miterlimit="',this.miterLimit,'"',' endcap="',processLineCap(this.lineCap),'"',' weight="',this.lineWidth,'px"',' color="',color,'" />')}lineStr.push("</g_vml_:shape>");this.element_.insertAdjacentHTML("beforeEnd",lineStr.join(""))};contextPrototype.fill=function(){this.stroke(true)};contextPrototype.closePath=function(){this.currentPath_.push({type:"close"})};contextPrototype.getCoords_=function(aX,aY){return{x:Z*(aX*this.m_[0][0]+aY*this.m_[1][0]+this.m_[2][0])-Z2,y:Z*(aX*this.m_[0][1]+aY*this.m_[1][1]+this.m_[2][1])-Z2}};contextPrototype.save=function(){var o={};copyState(this,o);this.aStack_.push(o);this.mStack_.push(this.m_);this.m_=matrixMultiply(createMatrixIdentity(),this.m_)};contextPrototype.restore=function(){copyState(this.aStack_.pop(),this);this.m_=this.mStack_.pop()};contextPrototype.translate=function(aX,aY){var m1=[[1,0,0],[0,1,0],[aX,aY,1]];this.m_=matrixMultiply(m1,this.m_)};contextPrototype.rotate=function(aRot){var c=mc(aRot);var s=ms(aRot);var m1=[[c,s,0],[-s,c,0],[0,0,1]];this.m_=matrixMultiply(m1,this.m_)};contextPrototype.scale=function(aX,aY){this.arcScaleX_*=aX;this.arcScaleY_*=aY;var m1=[[aX,0,0],[0,aY,0],[0,0,1]];this.m_=matrixMultiply(m1,this.m_)};contextPrototype.clip=function(){};contextPrototype.arcTo=function(){};contextPrototype.createPattern=function(){return new CanvasPattern_};function CanvasGradient_(aType){this.type_=aType;this.radius1_=0;this.radius2_=0;this.colors_=[];this.focus_={x:0,y:0}}CanvasGradient_.prototype.addColorStop=function(aOffset,aColor){aColor=processStyle(aColor);this.colors_.push({offset:1-aOffset,color:aColor})};function CanvasPattern_(){}G_vmlCanvasManager=G_vmlCanvasManager_;CanvasRenderingContext2D=CanvasRenderingContext2D_;CanvasGradient=CanvasGradient_;CanvasPattern=CanvasPattern_})()}

scrumburndownplugin/burndown/htdocs/js/graph.js

-/* This notice must remain at all times.
-
-graph.js
-Copyright (c) Balamurugan S, 2005. sbalamurugan @ hotmail.com
-Development support by Jexp, Inc http://www.jexp.com 
-
-This package is free software. It is distributed under GPL - legalese removed, it means that you can use this for any purpose, but cannot charge for this software. Any enhancements you make to this piece of code, should be made available free to the general public! 
-
-Latest version can be downloaded from http://www.sbmkpm.com
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
-
-graph.js provides a simple mechanism to draw bar graphs. It  uses 
-wz_jsgraphics.js  which is copyright of its author. 
-
-Usage:
-var g = new graph();
-g.add("title1",value);
-g.add("title2",value2);
-
-g.render("canvas_name", "graph title");
-
-//where canvas_name is a div defined INSIDE body tag 
-<body>
-<div id="canvas_name" style="overflow: auto; position:relative;height:300px;width:400px;"></div>
-
-
-*/
-
-hD="0123456789ABCDEF";
-
-function d2h(d) 
-{
- var h = hD.substr(d&15,1);
- while(d>15) {d>>=4;h=hD.substr(d&15,1)+h;}
- return h;
-}
-
-function h2d(h) 
-{
- return parseInt(h,16);
-}
-
-function graph()
-{
- this.ct = 0;
- 
- this.data      = new Array();
- this.x_name    = new Array();
- this.max       = -64000; //MAX INT
-
- this.c_array = new Array();
- this.c_array[0] = new Array(255, 192, 95);
- this.c_array[1] = new Array(80, 127, 175);
- this.c_array[2] = new Array(159, 87, 112);
- this.c_array[3] = new Array(111, 120, 96);
- this.c_array[4] = new Array(224, 119, 96);
- this.c_array[5] = new Array(80, 159, 144);
- this.c_array[6] = new Array(207, 88, 95);
- this.c_array[7] = new Array(64, 135, 96);
- this.c_array[8] = new Array(239, 160, 95);
- this.c_array[9] = new Array(144, 151, 80);
- this.c_array[10] = new Array(255, 136, 80);
-
- this.getColor = function()
-  {
-   if(this.ct >= (this.c_array.length-1))
-      this.ct = 0;
-   else
-      this.ct++;
-
-   return "#" + d2h(this.c_array[this.ct][0]) + d2h(this.c_array[this.ct][1]) + d2h(this.c_array[this.ct][2]);
-  }
-
-
- this.add = function(x_name, value)
-  {
-   this.x_name.push(x_name);  
-   this.data.push(parseInt(value,10));
-
-   if(value > this.max)
-      this.max = parseInt(value,10);
-  }
-
- this.render = function(canvas, title, height)
-  {
-   var jg = new jsGraphics(canvas);
-
-   var h  = 250;
-
-   if (typeof(height) == "number")
-       h = height;
-
-   var sx = 40;
-   var dw = 15;
-   var shadow = 3;
-   var fnt    = 12;
-
-   var rtmax = sx + 10 + (dw+Math.round((dw/2))+shadow)*(this.data.length);
-
-   // Draw markers
-   var i;
-   for(i = 1 ; i <= 5 ; i++)
-     {
-      jg.drawLine(0,Math.round((h/5*i)),rtmax+20,Math.round((h/5*i)));
-      var ff = Math.round(this.max - (this.max / 5 * i));
-      jg.drawString(ff+"",4,Math.round((h/5*i)-2));
-     }
-
-
-   // Draw the bar graph
-   for(i = 0; i < this.data.length; i++)
-      {
-       var ht1 = Math.round(this.data[i]*h/this.max);
-
-       // fix for wierd IE bug that doesn't display h=0
-       var ht2 = (ht1 > 0 ? ht1 : (ht1 == 0 ? 2 : ht1*-1));
-       if (ht1 < 0)
-           ht1 = 0;
-       
-       // shadow
-       jg.setColor("gray");
-       if(ht2-shadow > 1)
-          jg.fillRect(sx+shadow,h-ht1+shadow,dw,ht2-shadow);
-
-       jg.setColor(this.getColor());
-       jg.fillRect(sx,h-ht1,dw,ht2);
-
-       jg.setColor("black");
-       jg.drawRect(sx,h-ht1,dw,ht2);
-
-       jg.drawString(this.x_name[i], sx, h);
-
-       sx = sx+dw+Math.round(dw/2)+shadow;
-      }
-
-   jg.setFont("Verdana", fnt,  Font.BOLD);
-   jg.drawStringRect(title, 0, h+fnt+4, rtmax+20, "right");
-   jg.paint();
-  }
-
-}

scrumburndownplugin/burndown/htdocs/js/jquery.flot.js

-/* Javascript plotting library for jQuery, v. 0.5.
- *
- * Released under the MIT license by IOLA, December 2007.
- *
- */
-
-(function($) {
-    function Plot(target_, data_, options_) {
-        // data is on the form:
-        //   [ series1, series2 ... ]
-        // where series is either just the data as [ [x1, y1], [x2, y2], ... ]
-        // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label" }
-        
-        var series = [],
-            options = {
-            // the color theme used for graphs
-            colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],
-                legend: {
-                    show: true,
-                    noColumns: 1, // number of colums in legend table
-                    labelFormatter: null, // fn: string -> string
-                    labelBoxBorderColor: "#ccc", // border color for the little label boxes
-                    container: null, // container (as jQuery object) to put legend in, null means default on top of graph
-                    position: "ne", // position of default legend container within plot
-                    margin: 5, // distance from grid edge to default legend container within plot
-                    backgroundColor: null, // null means auto-detect
-                    backgroundOpacity: 0.85 // set to 0 to avoid background
-                },
-                xaxis: {
-                    mode: null, // null or "time"
-                    min: null, // min. value to show, null means set automatically
-                    max: null, // max. value to show, null means set automatically
-                    autoscaleMargin: null, // margin in % to add if auto-setting min/max
-                    ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
-                    tickFormatter: null, // fn: number -> string
-                    labelWidth: null, // size of tick labels in pixels
-                    labelHeight: null,
-                    
-                    // mode specific options
-                    tickDecimals: null, // no. of decimals, null means auto
-                    tickSize: null, // number or [number, "unit"]
-                    minTickSize: null, // number or [number, "unit"]
-                    monthNames: null, // list of names of months
-                    timeformat: null // format string to use
-                },
-                yaxis: {
-                    autoscaleMargin: 0.02
-                },
-                x2axis: {
-                    autoscaleMargin: null
-                },
-                y2axis: {
-                    autoscaleMargin: 0.02
-                },              
-                points: {
-                    show: false,
-                    radius: 3,
-                    lineWidth: 2, // in pixels
-                    fill: true,
-                    fillColor: "#ffffff"
-                },
-                lines: {
-                    show: false,
-                    lineWidth: 2, // in pixels
-                    fill: false,
-                    fillColor: null
-                },
-                bars: {
-                    show: false,
-                    lineWidth: 2, // in pixels
-                    barWidth: 1, // in units of the x axis
-                    fill: true,
-                    fillColor: null,
-                    align: "left" // or "center"
-                },
-                grid: {
-                    color: "#545454", // primary color used for outline and labels
-                    backgroundColor: null, // null for transparent, else color
-                    tickColor: "#dddddd", // color used for the ticks
-                    labelMargin: 5, // in pixels
-                    borderWidth: 2,
-                    markings: null, // array of ranges or fn: axes -> array of ranges
-                    markingsColor: "#f4f4f4",
-                    markingsLineWidth: 2,
-                    // interactive stuff
-                    clickable: false,
-                    hoverable: false,
-                    autoHighlight: true, // highlight in case mouse is near
-                    mouseActiveRadius: 10 // how far the mouse can be away to activate an item
-                },
-                selection: {
-                    mode: null, // one of null, "x", "y" or "xy"
-                    color: "#e8cfac"
-                },
-                shadowSize: 4
-            },
-        canvas = null,      // the canvas for the plot itself
-        overlay = null,     // canvas for interactive stuff on top of plot
-        eventHolder = null, // jQuery object that events should be bound to
-        ctx = null, octx = null,
-        target = target_,
-        axes = { xaxis: {}, yaxis: {}, x2axis: {}, y2axis: {} },
-        plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
-        canvasWidth = 0, canvasHeight = 0,
-        plotWidth = 0, plotHeight = 0,
-        // dedicated to storing data for buggy standard compliance cases
-        workarounds = {};
-        
-        this.setData = setData;
-        this.setupGrid = setupGrid;
-        this.draw = draw;
-        this.clearSelection = clearSelection;
-        this.setSelection = setSelection;
-        this.getCanvas = function() { return canvas; };
-        this.getPlotOffset = function() { return plotOffset; };
-        this.getData = function() { return series; };
-        this.getAxes = function() { return axes; };
-        this.highlight = highlight;
-        this.unhighlight = unhighlight;
-        
-        // initialize
-        parseOptions(options_);
-        setData(data_);
-        constructCanvas();
-        setupGrid();
-        draw();
-
-
-        function setData(d) {
-            series = parseData(d);
-
-            fillInSeriesOptions();
-            processData();
-        }
-        
-        function parseData(d) {
-            var res = [];
-            for (var i = 0; i < d.length; ++i) {
-                var s;
-                if (d[i].data) {
-                    s = {};
-                    for (var v in d[i])
-                        s[v] = d[i][v];
-                }
-                else {
-                    s = { data: d[i] };
-                }
-                res.push(s);
-            }
-
-            return res;
-        }
-        
-        function parseOptions(o) {
-            $.extend(true, options, o);
-
-            // backwards compatibility, to be removed in future
-            if (options.xaxis.noTicks && options.xaxis.ticks == null)
-                options.xaxis.ticks = options.xaxis.noTicks;
-            if (options.yaxis.noTicks && options.yaxis.ticks == null)
-                options.yaxis.ticks = options.yaxis.noTicks;
-            if (options.grid.coloredAreas)
-                options.grid.markings = options.grid.coloredAreas;
-            if (options.grid.coloredAreasColor)
-                options.grid.markingsColor = options.grid.coloredAreasColor;
-        }
-
-        function fillInSeriesOptions() {
-            var i;
-            
-            // collect what we already got of colors
-            var neededColors = series.length,
-                usedColors = [],
-                assignedColors = [];
-            for (i = 0; i < series.length; ++i) {
-                var sc = series[i].color;
-                if (sc != null) {
-                    --neededColors;
-                    if (typeof sc == "number")
-                        assignedColors.push(sc);
-                    else
-                        usedColors.push(parseColor(series[i].color));
-                }
-            }
-            
-            // we might need to generate more colors if higher indices
-            // are assigned
-            for (i = 0; i < assignedColors.length; ++i) {
-                neededColors = Math.max(neededColors, assignedColors[i] + 1);
-            }
-
-            // produce colors as needed
-            var colors = [], variation = 0;
-            i = 0;
-            while (colors.length < neededColors) {
-                var c;
-                if (options.colors.length == i) // check degenerate case
-                    c = new Color(100, 100, 100);
-                else
-                    c = parseColor(options.colors[i]);
-
-                // vary color if needed
-                var sign = variation % 2 == 1 ? -1 : 1;
-                var factor = 1 + sign * Math.ceil(variation / 2) * 0.2;
-                c.scale(factor, factor, factor);
-
-                // FIXME: if we're getting to close to something else,
-                // we should probably skip this one
-                colors.push(c);
-                
-                ++i;
-                if (i >= options.colors.length) {
-                    i = 0;
-                    ++variation;
-                }
-            }
-
-            // fill in the options
-            var colori = 0, s;
-            for (i = 0; i < series.length; ++i) {
-                s = series[i];
-
-                // assign colors
-                if (s.color == null) {
-                    s.color = colors[colori].toString();
-                    ++colori;
-                }
-                else if (typeof s.color == "number")
-                    s.color = colors[s.color].toString();
-
-                // copy the rest
-                s.lines = $.extend(true, {}, options.lines, s.lines);
-                s.points = $.extend(true, {}, options.points, s.points);
-                s.bars = $.extend(true, {}, options.bars, s.bars);
-                if (s.shadowSize == null)
-                    s.shadowSize = options.shadowSize;
-                if (s.xaxis && s.xaxis == 2)
-                    s.xaxis = axes.x2axis;
-                else
-                    s.xaxis = axes.xaxis;
-                if (s.yaxis && s.yaxis == 2)
-                    s.yaxis = axes.y2axis;
-                else
-                    s.yaxis = axes.yaxis;
-            }
-        }
-        
-        function processData() {
-            var topSentry = Number.POSITIVE_INFINITY,
-                bottomSentry = Number.NEGATIVE_INFINITY,
-                axis;
-
-            for (axis in axes) {
-                axes[axis].datamin = topSentry;
-                axes[axis].datamax = bottomSentry;
-                axes[axis].used = false;
-            }
-            
-            for (var i = 0; i < series.length; ++i) {
-                var data = series[i].data,
-                    axisx = series[i].xaxis,
-                    axisy = series[i].yaxis,
-                    mindelta = 0, maxdelta = 0;
-                
-                // make sure we got room for the bar
-                if (series[i].bars.show) {
-                    mindelta = series[i].bars.align == "left" ? 0 : -series[i].bars.barWidth/2;
-                    maxdelta = mindelta + series[i].bars.barWidth;
-                }
-                
-                axisx.used = axisy.used = true;
-                for (var j = 0; j < data.length; ++j) {
-                    if (data[j] == null)
-                        continue;
-                    
-                    var x = data[j][0], y = data[j][1];
-
-                    // convert to number
-                    if (x != null && !isNaN(x = +x)) {
-                        if (x + mindelta < axisx.datamin)
-                            axisx.datamin = x + mindelta;
-                        if (x + maxdelta > axisx.datamax)
-                            axisx.datamax = x + maxdelta;
-                    }
-                    
-                    if (y != null && !isNaN(y = +y)) {
-                        if (y < axisy.datamin)
-                            axisy.datamin = y;
-                        if (y > axisy.datamax)
-                            axisy.datamax = y;
-                    }
-                    
-                    if (x == null || y == null || isNaN(x) || isNaN(y))
-                        data[j] = null; // mark this point as invalid
-                }
-            }
-
-            for (axis in axes) {
-                if (axes[axis].datamin == topSentry)
-                    axes[axis].datamin = 0;
-                if (axes[axis].datamax == bottomSentry)
-                    axes[axis].datamax = 1;
-            }
-        }
-
-        function constructCanvas() {
-            canvasWidth = target.width();
-            canvasHeight = target.height();
-            target.html(""); // clear target
-            target.css("position", "relative"); // for positioning labels and overlay
-
-            if (canvasWidth <= 0 || canvasHeight <= 0)
-                throw "Invalid dimensions for plot, width = " + canvasWidth + ", height = " + canvasHeight;
-
-            // the canvas
-            canvas = $('<canvas width="' + canvasWidth + '" height="' + canvasHeight + '"></canvas>').appendTo(target).get(0);
-            if ($.browser.msie) // excanvas hack
-                canvas = window.G_vmlCanvasManager.initElement(canvas);
-            ctx = canvas.getContext("2d");
-
-            // overlay canvas for interactive features
-            overlay = $('<canvas style="position:absolute;left:0px;top:0px;" width="' + canvasWidth + '" height="' + canvasHeight + '"></canvas>').appendTo(target).get(0);
-            if ($.browser.msie) // excanvas hack
-                overlay = window.G_vmlCanvasManager.initElement(overlay);
-            octx = overlay.getContext("2d");
-
-            // we include the canvas in the event holder too, because IE 7
-            // sometimes has trouble with the stacking order
-            eventHolder = $([overlay, canvas]);
-
-            // bind events
-            if (options.selection.mode != null || options.grid.hoverable) {
-                // FIXME: temp. work-around until jQuery bug 1871 is fixed
-                eventHolder.each(function () {
-                    this.onmousemove = onMouseMove;
-                });
-
-                if (options.selection.mode != null)
-                    eventHolder.mousedown(onMouseDown);
-            }
-
-            if (options.grid.clickable)
-                eventHolder.click(onClick);
-        }
-
-        function setupGrid() {
-            function setupAxis(axis, options) {
-                setRange(axis, options);
-                prepareTickGeneration(axis, options);
-                setTicks(axis, options);
-                // add transformation helpers
-                if (axis == axes.xaxis || axis == axes.x2axis) {
-                    // data point to canvas coordinate
-                    axis.p2c = function (p) { return (p - axis.min) * axis.scale; };
-                    // canvas coordinate to data point 
-                    axis.c2p = function (c) { return axis.min + c / axis.scale; };
-                }
-                else {
-                    axis.p2c = function (p) { return (axis.max - p) * axis.scale; };
-                    axis.c2p = function (p) { return axis.max - p / axis.scale; };
-                }
-            }
-
-            for (var axis in axes)
-                setupAxis(axes[axis], options[axis]);
-
-            setSpacing();
-            insertLabels();
-            insertLegend();
-        }
-        
-        function setRange(axis, axisOptions) {
-            var min = axisOptions.min != null ? axisOptions.min : axis.datamin;
-            var max = axisOptions.max != null ? axisOptions.max : axis.datamax;
-
-            if (max - min == 0.0) {
-                // degenerate case
-                var widen;
-                if (max == 0.0)
-                    widen = 1.0;
-                else
-                    widen = 0.01;
-
-                min -= widen;
-                max += widen;
-            }
-            else {
-                // consider autoscaling
-                var margin = axisOptions.autoscaleMargin;
-                if (margin != null) {
-                    if (axisOptions.min == null) {
-                        min -= (max - min) * margin;
-                        // make sure we don't go below zero if all values
-                        // are positive
-                        if (min < 0 && axis.datamin >= 0)
-                            min = 0;
-                    }
-                    if (axisOptions.max == null) {
-                        max += (max - min) * margin;
-                        if (max > 0 && axis.datamax <= 0)
-                            max = 0;
-                    }
-                }
-            }
-            axis.min = min;
-            axis.max = max;
-        }
-
-        function prepareTickGeneration(axis, axisOptions) {
-            // estimate number of ticks
-            var noTicks;
-            if (typeof axisOptions.ticks == "number" && axisOptions.ticks > 0)
-                noTicks = axisOptions.ticks;
-            else if (axis == axes.xaxis || axis == axes.x2axis)
-                noTicks = canvasWidth / 100;
-            else
-                noTicks = canvasHeight / 60;
-            
-            var delta = (axis.max - axis.min) / noTicks;
-            var size, generator, unit, formatter, i, magn, norm;
-
-            if (axisOptions.mode == "time") {
-                // pretty handling of time
-                
-                function formatDate(d, fmt, monthNames) {
-                    var leftPad = function(n) {
-                        n = "" + n;
-                        return n.length == 1 ? "0" + n : n;
-                    };
-                    
-                    var r = [];
-                    var escape = false;
-                    if (monthNames == null)
-                        monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
-                    for (var i = 0; i < fmt.length; ++i) {
-                        var c = fmt.charAt(i);
-                        
-                        if (escape) {
-                            switch (c) {
-                            case 'h': c = "" + d.getUTCHours(); break;
-                            case 'H': c = leftPad(d.getUTCHours()); break;
-                            case 'M': c = leftPad(d.getUTCMinutes()); break;
-                            case 'S': c = leftPad(d.getUTCSeconds()); break;
-                            case 'd': c = "" + d.getUTCDate(); break;
-                            case 'm': c = "" + (d.getUTCMonth() + 1); break;
-                            case 'y': c = "" + d.getUTCFullYear(); break;
-                            case 'b': c = "" + monthNames[d.getUTCMonth()]; break;
-                            }
-                            r.push(c);
-                            escape = false;
-                        }
-                        else {
-                            if (c == "%")
-                                escape = true;
-                            else
-                                r.push(c);
-                        }
-                    }
-                    return r.join("");
-                }
-                
-                    
-                // map of app. size of time units in milliseconds
-                var timeUnitSize = {
-                    "second": 1000,
-                    "minute": 60 * 1000,
-                    "hour": 60 * 60 * 1000,
-                    "day": 24 * 60 * 60 * 1000,
-                    "month": 30 * 24 * 60 * 60 * 1000,
-                    "year": 365.2425 * 24 * 60 * 60 * 1000
-                };
-
-
-                // the allowed tick sizes, after 1 year we use
-                // an integer algorithm
-                var spec = [
-                    [1, "second"], [2, "second"], [5, "second"], [10, "second"],
-                    [30, "second"], 
-                    [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
-                    [30, "minute"], 
-                    [1, "hour"], [2, "hour"], [4, "hour"],
-                    [8, "hour"], [12, "hour"],
-                    [1, "day"], [2, "day"], [3, "day"],
-                    [0.25, "month"], [0.5, "month"], [1, "month"],
-                    [2, "month"], [3, "month"], [6, "month"],
-                    [1, "year"]
-                ];
-
-                var minSize = 0;
-                if (axisOptions.minTickSize != null) {
-                    if (typeof axisOptions.tickSize == "number")
-                        minSize = axisOptions.tickSize;
-                    else
-                        minSize = axisOptions.minTickSize[0] * timeUnitSize[axisOptions.minTickSize[1]];
-                }
-
-                for (i = 0; i < spec.length - 1; ++i)
-                    if (delta < (spec[i][0] * timeUnitSize[spec[i][1]]
-                                 + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
-                       && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize)
-                        break;
-                size = spec[i][0];
-                unit = spec[i][1];
-                
-                // special-case the possibility of several years
-                if (unit == "year") {
-                    magn = Math.pow(10, Math.floor(Math.log(delta / timeUnitSize.year) / Math.LN10));
-                    norm = (delta / timeUnitSize.year) / magn;
-                    if (norm < 1.5)
-                        size = 1;
-                    else if (norm < 3)
-                        size = 2;
-                    else if (norm < 7.5)
-                        size = 5;
-                    else
-                        size = 10;
-
-                    size *= magn;
-                }
-
-                if (axisOptions.tickSize) {
-                    size = axisOptions.tickSize[0];
-                    unit = axisOptions.tickSize[1];
-                }
-                
-                generator = function(axis) {
-                    var ticks = [],
-                        tickSize = axis.tickSize[0], unit = axis.tickSize[1],
-                        d = new Date(axis.min);
-                    
-                    var step = tickSize * timeUnitSize[unit];
-
-                    if (unit == "second")
-                        d.setUTCSeconds(floorInBase(d.getUTCSeconds(), tickSize));
-                    if (unit == "minute")
-                        d.setUTCMinutes(floorInBase(d.getUTCMinutes(), tickSize));
-                    if (unit == "hour")
-                        d.setUTCHours(floorInBase(d.getUTCHours(), tickSize));
-                    if (unit == "month")
-                        d.setUTCMonth(floorInBase(d.getUTCMonth(), tickSize));
-                    if (unit == "year")
-                        d.setUTCFullYear(floorInBase(d.getUTCFullYear(), tickSize));
-                    
-                    // reset smaller components
-                    d.setUTCMilliseconds(0);
-                    if (step >= timeUnitSize.minute)
-                        d.setUTCSeconds(0);
-                    if (step >= timeUnitSize.hour)
-                        d.setUTCMinutes(0);
-                    if (step >= timeUnitSize.day)
-                        d.setUTCHours(0);
-                    if (step >= timeUnitSize.day * 4)
-                        d.setUTCDate(1);
-                    if (step >= timeUnitSize.year)
-                        d.setUTCMonth(0);
-
-
-                    var carry = 0, v = Number.NaN, prev;
-                    do {
-                        prev = v;
-                        v = d.getTime();
-                        ticks.push({ v: v, label: axis.tickFormatter(v, axis) });
-                        if (unit == "month") {
-                            if (tickSize < 1) {
-                                // a bit complicated - we'll divide the month
-                                // up but we need to take care of fractions
-                                // so we don't end up in the middle of a day
-                                d.setUTCDate(1);
-                                var start = d.getTime();
-                                d.setUTCMonth(d.getUTCMonth() + 1);
-                                var end = d.getTime();
-                                d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
-                                carry = d.getUTCHours();
-                                d.setUTCHours(0);
-                            }
-                            else
-                                d.setUTCMonth(d.getUTCMonth() + tickSize);
-                        }
-                        else if (unit == "year") {
-                            d.setUTCFullYear(d.getUTCFullYear() + tickSize);
-                        }
-                        else
-                            d.setTime(v + step);
-                    } while (v < axis.max && v != prev);
-
-                    return ticks;
-                };
-
-                formatter = function (v, axis) {
-                    var d = new Date(v);
-
-                    // first check global format
-                    if (axisOptions.timeformat != null)
-                        return formatDate(d, axisOptions.timeformat, axisOptions.monthNames);
-                    
-                    var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
-                    var span = axis.max - axis.min;
-                    
-                    if (t < timeUnitSize.minute)
-                        fmt = "%h:%M:%S";
-                    else if (t < timeUnitSize.day) {
-                        if (span < 2 * timeUnitSize.day)
-                            fmt = "%h:%M";
-                        else
-                            fmt = "%b %d %h:%M";
-                    }
-                    else if (t < timeUnitSize.month)
-                        fmt = "%b %d";
-                    else if (t < timeUnitSize.year) {
-                        if (span < timeUnitSize.year)
-                            fmt = "%b";
-                        else
-                            fmt = "%b %y";
-                    }
-                    else
-                        fmt = "%y";
-                    
-                    return formatDate(d, fmt, axisOptions.monthNames);
-                };
-            }
-            else {
-                // pretty rounding of base-10 numbers
-                var maxDec = axisOptions.tickDecimals;
-                var dec = -Math.floor(Math.log(delta) / Math.LN10);
-                if (maxDec != null && dec > maxDec)
-                    dec = maxDec;
-                
-                magn = Math.pow(10, -dec);
-                norm = delta / magn; // norm is between 1.0 and 10.0
-                
-                if (norm < 1.5)
-                    size = 1;
-                else if (norm < 3) {
-                    size = 2;
-                    // special case for 2.5, requires an extra decimal
-                    if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) {
-                        size = 2.5;
-                        ++dec;
-                    }
-                }
-                else if (norm < 7.5)
-                    size = 5;
-                else
-                    size = 10;
-
-                size *= magn;
-                
-                if (axisOptions.minTickSize != null && size < axisOptions.minTickSize)
-                    size = axisOptions.minTickSize;
-
-                if (axisOptions.tickSize != null)
-                    size = axisOptions.tickSize;
-                
-                axis.tickDecimals = Math.max(0, (maxDec != null) ? maxDec : dec);
-                
-                generator = function (axis) {
-                    var ticks = [];
-
-                    // spew out all possible ticks
-                    var start = floorInBase(axis.min, axis.tickSize),
-                        i = 0, v = Number.NaN, prev;
-                    do {
-                        prev = v;
-                        v = start + i * axis.tickSize;
-                        ticks.push({ v: v, label: axis.tickFormatter(v, axis) });
-                        ++i;
-                    } while (v < axis.max && v != prev);
-                    return ticks;
-                };
-
-                formatter = function (v, axis) {
-                    return v.toFixed(axis.tickDecimals);
-                };
-            }
-
-            axis.tickSize = unit ? [size, unit] : size;
-            axis.tickGenerator = generator;
-            if ($.isFunction(axisOptions.tickFormatter))
-                axis.tickFormatter = function (v, axis) { return "" + axisOptions.tickFormatter(v, axis); };
-            else
-                axis.tickFormatter = formatter;
-            if (axisOptions.labelWidth != null)
-                axis.labelWidth = axisOptions.labelWidth;
-            if (axisOptions.labelHeight != null)
-                axis.labelHeight = axisOptions.labelHeight;
-        }
-        
-        function setTicks(axis, axisOptions) {
-            axis.ticks = [];
-
-            if (!axis.used)
-                return;
-            
-            if (axisOptions.ticks == null)
-                axis.ticks = axis.tickGenerator(axis);
-            else if (typeof axisOptions.ticks == "number") {
-                if (axisOptions.ticks > 0)
-                    axis.ticks = axis.tickGenerator(axis);
-            }
-            else if (axisOptions.ticks) {
-                var ticks = axisOptions.ticks;
-
-                if ($.isFunction(ticks))
-                    // generate the ticks
-                    ticks = ticks({ min: axis.min, max: axis.max });
-                
-                // clean up the user-supplied ticks, copy them over
-                var i, v;
-                for (i = 0; i < ticks.length; ++i) {
-                    var label = null;
-                    var t = ticks[i];
-                    if (typeof t == "object") {
-                        v = t[0];
-                        if (t.length > 1)
-                            label = t[1];
-                    }
-                    else
-                        v = t;
-                    if (label == null)
-                        label = axis.tickFormatter(v, axis);
-                    axis.ticks[i] = { v: v, label: label };
-                }
-            }
-
-            if (axisOptions.autoscaleMargin != null && axis.ticks.length > 0) {
-                // snap to ticks
-                if (axisOptions.min == null)
-                    axis.min = Math.min(axis.min, axis.ticks[0].v);
-                if (axisOptions.max == null && axis.ticks.length > 1)
-                    axis.max = Math.min(axis.max, axis.ticks[axis.ticks.length - 1].v);
-            }
-        }
-        
-        function setSpacing() {
-            function measureXLabels(axis) {
-                // to avoid measuring the widths of the labels, we
-                // construct fixed-size boxes and put the labels inside
-                // them, we don't need the exact figures and the
-                // fixed-size box content is easy to center
-                if (axis.labelWidth == null)
-                    axis.labelWidth = canvasWidth / 6;
-
-                // measure x label heights
-                if (axis.labelHeight == null) {
-                    labels = [];
-                    for (i = 0; i < axis.ticks.length; ++i) {
-                        l = axis.ticks[i].label;
-                        if (l)
-                            labels.push('<div class="tickLabel" style="float:left;width:' + axis.labelWidth + 'px">' + l + '</div>');
-                    }
-                    
-                    axis.labelHeight = 0;
-                    if (labels.length > 0) {
-                        var dummyDiv = $('<div style="position:absolute;top:-10000px;width:10000px;font-size:smaller">'
-                                         + labels.join("") + '<div style="clear:left"></div></div>').appendTo(target);
-                        axis.labelHeight = dummyDiv.height();
-                        dummyDiv.remove();
-                    }
-                }
-            }
-            
-            function measureYLabels(axis) {
-                if (axis.labelWidth == null || axis.labelHeight == null) {
-                    var i, labels = [], l;
-                    // calculate y label dimensions
-                    for (i = 0; i < axis.ticks.length; ++i) {
-                        l = axis.ticks[i].label;
-                        if (l)
-                            labels.push('<div class="tickLabel">' + l + '</div>');
-                    }
-                    
-                    if (labels.length > 0) {
-                        var dummyDiv = $('<div style="position:absolute;top:-10000px;font-size:smaller">'
-                                         + labels.join("") + '</div>').appendTo(target);
-                        if (axis.labelWidth == null)
-                            axis.labelWidth = dummyDiv.width();
-                        if (axis.labelHeight == null)
-                            axis.labelHeight = dummyDiv.find("div").height();
-                        dummyDiv.remove();
-                    }
-                    
-                    if (axis.labelWidth == null)
-                        axis.labelWidth = 0;
-                    if (axis.labelHeight == null)
-                        axis.labelHeight = 0;
-                }
-            }
-            
-            measureXLabels(axes.xaxis);
-            measureYLabels(axes.yaxis);
-            measureXLabels(axes.x2axis);
-            measureYLabels(axes.y2axis);
-
-            // get the most space needed around the grid for things
-            // that may stick out
-            var maxOutset = options.grid.borderWidth / 2;
-            for (i = 0; i < series.length; ++i)
-                maxOutset = Math.max(maxOutset, 2 * (series[i].points.radius + series[i].points.lineWidth/2));
-
-            plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = maxOutset;
-
-            if (axes.xaxis.labelHeight > 0)
-                plotOffset.bottom = Math.max(maxOutset, axes.xaxis.labelHeight + options.grid.labelMargin);
-            if (axes.yaxis.labelWidth > 0)
-                plotOffset.left = Math.max(maxOutset, axes.yaxis.labelWidth + options.grid.labelMargin);
-
-            if (axes.x2axis.labelHeight > 0)
-                plotOffset.top = Math.max(maxOutset, axes.x2axis.labelHeight + options.grid.labelMargin);
-            
-            if (axes.y2axis.labelWidth > 0)
-                plotOffset.right = Math.max(maxOutset, axes.y2axis.labelWidth + options.grid.labelMargin);
-
-            plotWidth = canvasWidth - plotOffset.left - plotOffset.right;
-            plotHeight = canvasHeight - plotOffset.bottom - plotOffset.top;
-
-            // precompute how much the axis is scaling a point in canvas space
-            axes.xaxis.scale = plotWidth / (axes.xaxis.max - axes.xaxis.min);
-            axes.yaxis.scale = plotHeight / (axes.yaxis.max - axes.yaxis.min);
-            axes.x2axis.scale = plotWidth / (axes.x2axis.max - axes.x2axis.min);
-            axes.y2axis.scale = plotHeight / (axes.y2axis.max - axes.y2axis.min);
-        }
-        
-        function draw() {
-            drawGrid();
-            for (var i = 0; i < series.length; i++) {
-                drawSeries(series[i]);
-            }
-        }
-
-        function extractRange(ranges, coord) {
-            var firstAxis = coord + "axis",
-                secondaryAxis = coord + "2axis",
-                axis, from, to, reverse;
-
-            if (ranges[firstAxis]) {
-                axis = axes[firstAxis];
-                from = ranges[firstAxis].from;
-                to = ranges[firstAxis].to;
-            }
-            else if (ranges[secondaryAxis]) {
-                axis = axes[secondaryAxis];
-                from = ranges[secondaryAxis].from;
-                to = ranges[secondaryAxis].to;
-            }
-            else {
-                // backwards-compat stuff - to be removed in future
-                axis = axes[firstAxis];
-                from = ranges[coord + "1"];
-                to = ranges[coord + "2"];
-            }
-
-            // auto-reverse as an added bonus
-            if (from != null && to != null && from > to)
-                return { from: to, to: from, axis: axis };
-            
-            return { from: from, to: to, axis: axis };
-        }
-        
-        function drawGrid() {
-            var i;
-            
-            ctx.save();
-            ctx.clearRect(0, 0, canvasWidth, canvasHeight);
-            ctx.translate(plotOffset.left, plotOffset.top);
-
-            // draw background, if any
-            if (options.grid.backgroundColor) {
-                ctx.fillStyle = options.grid.backgroundColor;
-                ctx.fillRect(0, 0, plotWidth, plotHeight);
-            }
-
-            // draw markings
-            if (options.grid.markings) {
-                var markings = options.grid.markings;
-                if ($.isFunction(markings))
-                    // xmin etc. are backwards-compatible, to be removed in future
-                    markings = markings({ xmin: axes.xaxis.min, xmax: axes.xaxis.max, ymin: axes.yaxis.min, ymax: axes.yaxis.max, xaxis: axes.xaxis, yaxis: axes.yaxis, x2axis: axes.x2axis, y2axis: axes.y2axis });
-
-                for (i = 0; i < markings.length; ++i) {
-                    var m = markings[i],
-                        xrange = extractRange(m, "x"),
-                        yrange = extractRange(m, "y");
-
-                    // fill in missing
-                    if (xrange.from == null)
-                        xrange.from = xrange.axis.min;
-                    if (xrange.to == null)
-                        xrange.to = xrange.axis.max;
-                    if (yrange.from == null)
-                        yrange.from = yrange.axis.min;
-                    if (yrange.to == null)
-                        yrange.to = yrange.axis.max;
-
-                    // clip
-                    if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||
-                        yrange.to < yrange.axis.min || yrange.from > yrange.axis.max)
-                        continue;
-
-                    xrange.from = Math.max(xrange.from, xrange.axis.min);
-                    xrange.to = Math.min(xrange.to, xrange.axis.max);
-                    yrange.from = Math.max(yrange.from, yrange.axis.min);
-                    yrange.to = Math.min(yrange.to, yrange.axis.max);
-
-                    if (xrange.from == xrange.to && yrange.from == yrange.to)
-                        continue;
-
-                    // then draw
-                    xrange.from = xrange.axis.p2c(xrange.from);
-                    xrange.to = xrange.axis.p2c(xrange.to);
-                    yrange.from = yrange.axis.p2c(yrange.from);
-                    yrange.to = yrange.axis.p2c(yrange.to);
-                    
-                    if (xrange.from == xrange.to || yrange.from == yrange.to) {
-                        // draw line
-                        ctx.strokeStyle = m.color || options.grid.markingsColor;
-                        ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;
-                        ctx.moveTo(Math.floor(xrange.from), Math.floor(yrange.from));
-                        ctx.lineTo(Math.floor(xrange.to), Math.floor(yrange.to));
-                        ctx.stroke();
-                    }
-                    else {
-                        // fill area
-                        ctx.fillStyle = m.color || options.grid.markingsColor;
-                        ctx.fillRect(Math.floor(xrange.from),
-                                     Math.floor(yrange.to),
-                                     Math.floor(xrange.to - xrange.from),
-                                     Math.floor(yrange.from - yrange.to));
-                    }
-                }
-            }
-            
-            // draw the inner grid
-            ctx.lineWidth = 1;
-            ctx.strokeStyle = options.grid.tickColor;
-            ctx.beginPath();
-            var v, axis = axes.xaxis;
-            for (i = 0; i < axis.ticks.length; ++i) {
-                v = axis.ticks[i].v;
-                if (v <= axis.min || v >= axes.xaxis.max)
-                    continue;   // skip those lying on the axes
-
-                ctx.moveTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, 0);
-                ctx.lineTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, plotHeight);
-            }
-
-            axis = axes.yaxis;
-            for (i = 0; i < axis.ticks.length; ++i) {
-                v = axis.ticks[i].v;
-                if (v <= axis.min || v >= axis.max)
-                    continue;
-
-                ctx.moveTo(0, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
-                ctx.lineTo(plotWidth, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
-            }
-
-            axis = axes.x2axis;
-            for (i = 0; i < axis.ticks.length; ++i) {
-                v = axis.ticks[i].v;
-                if (v <= axis.min || v >= axis.max)
-                    continue;
-    
-                ctx.moveTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, -5);
-                ctx.lineTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, 5);
-            }
-
-            axis = axes.y2axis;
-            for (i = 0; i < axis.ticks.length; ++i) {
-                v = axis.ticks[i].v;
-                if (v <= axis.min || v >= axis.max)
-                    continue;
-
-                ctx.moveTo(plotWidth-5, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
-                ctx.lineTo(plotWidth+5, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
-            }
-            
-            ctx.stroke();
-            
-            if (options.grid.borderWidth) {
-                // draw border
-                ctx.lineWidth = options.grid.borderWidth;
-                ctx.strokeStyle = options.grid.color;
-                ctx.lineJoin = "round";
-                ctx.strokeRect(0, 0, plotWidth, plotHeight);
-            }
-
-            ctx.restore();
-        }
-        
-        function insertLabels() {
-            target.find(".tickLabels").remove();
-            
-            var html = '<div class="tickLabels" style="font-size:smaller;color:' + options.grid.color + '">';
-
-            function addLabels(axis, labelGenerator) {
-                for (var i = 0; i < axis.ticks.length; ++i) {
-                    var tick = axis.ticks[i];
-                    if (!tick.label || tick.v < axis.min || tick.v > axis.max)
-                        continue;
-                    html += labelGenerator(tick, axis);
-                }
-            }
-            
-            addLabels(axes.xaxis, function (tick, axis) {
-                return '<div style="position:absolute;top:' + (plotOffset.top + plotHeight + options.grid.labelMargin) + 'px;left:' + (plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2) + 'px;width:' + axis.labelWidth + 'px;text-align:center" class="tickLabel">' + tick.label + "</div>";
-            });
-            
-            
-            addLabels(axes.yaxis, function (tick, axis) {
-                return '<div style="position:absolute;top:' + (plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2) + 'px;right:' + (plotOffset.right + plotWidth + options.grid.labelMargin) + 'px;width:' + axis.labelWidth + 'px;text-align:right" class="tickLabel">' + tick.label + "</div>";
-            });
-            
-            addLabels(axes.x2axis, function (tick, axis) {
-                return '<div style="position:absolute;bottom:' + (plotOffset.bottom + plotHeight + options.grid.labelMargin) + 'px;left:' + (plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2) + 'px;width:' + axis.labelWidth + 'px;text-align:center" class="tickLabel">' + tick.label + "</div>";
-            });