initStyles = function() {

addCSS(defaultStyles);

};

$(initStyles);

pending = []; $.fn.sparkline = function (userValues, userOptions) {

return this.each(function () {
    var options = new $.fn.sparkline.options(this, userOptions),
         $this = $(this),
         render, i;
    render = function () {
        var values, width, height, tmp, mhandler, sp, vals;
        if (userValues === 'html' || userValues === undefined) {
            vals = this.getAttribute(options.get('tagValuesAttribute'));
            if (vals === undefined || vals === null) {
                vals = $this.html();
            }
            values = vals.replace(/(^\s*<!--)|(-->\s*$)|\s+/g, '').split(',');
        } else {
            values = userValues;
        }

        width = options.get('width') === 'auto' ? values.length * options.get('defaultPixelsPerValue') : options.get('width');
        if (options.get('height') === 'auto') {
            if (!options.get('composite') || !$.data(this, '_jqs_vcanvas')) {
                // must be a better way to get the line height
                tmp = document.createElement('span');
                tmp.innerHTML = 'a';
                $this.html(tmp);
                height = $(tmp).innerHeight() || $(tmp).height();
                $(tmp).remove();
                tmp = null;
            }
        } else {
            height = options.get('height');
        }

        if (!options.get('disableInteraction')) {
            mhandler = $.data(this, '_jqs_mhandler');
            if (!mhandler) {
                mhandler = new MouseHandler(this, options);
                $.data(this, '_jqs_mhandler', mhandler);
            } else if (!options.get('composite')) {
                mhandler.reset();
            }
        } else {
            mhandler = false;
        }

        if (options.get('composite') && !$.data(this, '_jqs_vcanvas')) {
            if (!$.data(this, '_jqs_errnotify')) {
                alert('Attempted to attach a composite sparkline to an element with no existing sparkline');
                $.data(this, '_jqs_errnotify', true);
            }
            return;
        }

        sp = new $.fn.sparkline[options.get('type')](this, values, options, width, height);

        sp.render();

        if (mhandler) {
            mhandler.registerSparkline(sp);
        }
    };
    if (($(this).html() && !options.get('disableHiddenCheck') && $(this).is(':hidden')) || !$(this).parents('body').length) {
        if (!options.get('composite') && $.data(this, '_jqs_pending')) {
            // remove any existing references to the element
            for (i = pending.length; i; i--) {
                if (pending[i - 1][0] == this) {
                    pending.splice(i - 1, 1);
                }
            }
        }
        pending.push([this, render]);
        $.data(this, '_jqs_pending', true);
    } else {
        render.call(this);
    }
});

};

$.fn.sparkline.defaults = getDefaults();

$.sparkline_display_visible = function () {

var el, i, pl;
var done = [];
for (i = 0, pl = pending.length; i < pl; i++) {
    el = pending[i][0];
    if ($(el).is(':visible') && !$(el).parents().is(':hidden')) {
        pending[i][1].call(el);
        $.data(pending[i][0], '_jqs_pending', false);
        done.push(i);
    } else if (!$(el).closest('html').length && !$.data(el, '_jqs_pending')) {
        // element has been inserted and removed from the DOM
        // If it was not yet inserted into the dom then the .data request
        // will return true.
        // removing from the dom causes the data to be removed.
        $.data(pending[i][0], '_jqs_pending', false);
        done.push(i);
    }
}
for (i = done.length; i; i--) {
    pending.splice(done[i - 1], 1);
}

};

/**

* User option handler
*/

$.fn.sparkline.options = createClass({

init: function (tag, userOptions) {
    var extendedOptions, defaults, base, tagOptionType;
    this.userOptions = userOptions = userOptions || {};
    this.tag = tag;
    this.tagValCache = {};
    defaults = $.fn.sparkline.defaults;
    base = defaults.common;
    this.tagOptionsPrefix = userOptions.enableTagOptions && (userOptions.tagOptionsPrefix || base.tagOptionsPrefix);

    tagOptionType = this.getTagSetting('type');
    if (tagOptionType === UNSET_OPTION) {
        extendedOptions = defaults[userOptions.type || base.type];
    } else {
        extendedOptions = defaults[tagOptionType];
    }
    this.mergedOptions = $.extend({}, base, extendedOptions, userOptions);
},

getTagSetting: function (key) {
    var prefix = this.tagOptionsPrefix,
        val, i, pairs, keyval;
    if (prefix === false || prefix === undefined) {
        return UNSET_OPTION;
    }
    if (this.tagValCache.hasOwnProperty(key)) {
        val = this.tagValCache.key;
    } else {
        val = this.tag.getAttribute(prefix + key);
        if (val === undefined || val === null) {
            val = UNSET_OPTION;
        } else if (val.substr(0, 1) === '[') {
            val = val.substr(1, val.length - 2).split(',');
            for (i = val.length; i--;) {
                val[i] = normalizeValue(val[i].replace(/(^\s*)|(\s*$)/g, ''));
            }
        } else if (val.substr(0, 1) === '{') {
            pairs = val.substr(1, val.length - 2).split(',');
            val = {};
            for (i = pairs.length; i--;) {
                keyval = pairs[i].split(':', 2);
                val[keyval[0].replace(/(^\s*)|(\s*$)/g, '')] = normalizeValue(keyval[1].replace(/(^\s*)|(\s*$)/g, ''));
            }
        } else {
            val = normalizeValue(val);
        }
        this.tagValCache.key = val;
    }
    return val;
},

get: function (key, defaultval) {
    var tagOption = this.getTagSetting(key),
        result;
    if (tagOption !== UNSET_OPTION) {
        return tagOption;
    }
    return (result = this.mergedOptions[key]) === undefined ? defaultval : result;
}

});

$.fn.sparkline._base = createClass({

disabled: false,

init: function (el, values, options, width, height) {
    this.el = el;
    this.$el = $(el);
    this.values = values;
    this.options = options;
    this.width = width;
    this.height = height;
    this.currentRegion = undefined;
},

/**
 * Setup the canvas
 */
initTarget: function () {
    var interactive = !this.options.get('disableInteraction');
    if (!(this.target = this.$el.simpledraw(this.width, this.height, this.options.get('composite'), interactive))) {
        this.disabled = true;
    } else {
        this.canvasWidth = this.target.pixelWidth;
        this.canvasHeight = this.target.pixelHeight;
    }
},

/**
 * Actually render the chart to the canvas
 */
render: function () {
    if (this.disabled) {
        this.el.innerHTML = '';
        return false;
    }
    return true;
},

/**
 * Return a region id for a given x/y co-ordinate
 */
getRegion: function (x, y) {
},

/**
 * Highlight an item based on the moused-over x,y co-ordinate
 */
setRegionHighlight: function (el, x, y) {
    var currentRegion = this.currentRegion,
        highlightEnabled = !this.options.get('disableHighlight'),
        newRegion;
    if (x > this.canvasWidth || y > this.canvasHeight || x < 0 || y < 0) {
        return null;
    }
    newRegion = this.getRegion(el, x, y);
    if (currentRegion !== newRegion) {
        if (currentRegion !== undefined && highlightEnabled) {
            this.removeHighlight();
        }
        this.currentRegion = newRegion;
        if (newRegion !== undefined && highlightEnabled) {
            this.renderHighlight();
        }
        return true;
    }
    return false;
},

/**
 * Reset any currently highlighted item
 */
clearRegionHighlight: function () {
    if (this.currentRegion !== undefined) {
        this.removeHighlight();
        this.currentRegion = undefined;
        return true;
    }
    return false;
},

renderHighlight: function () {
    this.changeHighlight(true);
},

removeHighlight: function () {
    this.changeHighlight(false);
},

changeHighlight: function (highlight)  {},

/**
 * Fetch the HTML to display as a tooltip
 */
getCurrentRegionTooltip: function () {
    var options = this.options,
        header = '',
        entries = [],
        fields, formats, formatlen, fclass, text, i,
        showFields, showFieldsKey, newFields, fv,
        formatter, format, fieldlen, j;
    if (this.currentRegion === undefined) {
        return '';
    }
    fields = this.getCurrentRegionFields();
    formatter = options.get('tooltipFormatter');
    if (formatter) {
        return formatter(this, options, fields);
    }
    if (options.get('tooltipChartTitle')) {
        header += '<div class="jqs jqstitle">' + options.get('tooltipChartTitle') + '</div>\n';
    }
    formats = this.options.get('tooltipFormat');
    if (!formats) {
        return '';
    }
    if (!$.isArray(formats)) {
        formats = [formats];
    }
    if (!$.isArray(fields)) {
        fields = [fields];
    }
    showFields = this.options.get('tooltipFormatFieldlist');
    showFieldsKey = this.options.get('tooltipFormatFieldlistKey');
    if (showFields && showFieldsKey) {
        // user-selected ordering of fields
        newFields = [];
        for (i = fields.length; i--;) {
            fv = fields[i][showFieldsKey];
            if ((j = $.inArray(fv, showFields)) != -1) {
                newFields[j] = fields[i];
            }
        }
        fields = newFields;
    }
    formatlen = formats.length;
    fieldlen = fields.length;
    for (i = 0; i < formatlen; i++) {
        format = formats[i];
        if (typeof format === 'string') {
            format = new SPFormat(format);
        }
        fclass = format.fclass || 'jqsfield';
        for (j = 0; j < fieldlen; j++) {
            if (!fields[j].isNull || !options.get('tooltipSkipNull')) {
                $.extend(fields[j], {
                    prefix: options.get('tooltipPrefix'),
                    suffix: options.get('tooltipSuffix')
                });
                text = format.render(fields[j], options.get('tooltipValueLookups'), options);
                entries.push('<div class="' + fclass + '">' + text + '</div>');
            }
        }
    }
    if (entries.length) {
        return header + entries.join('\n');
    }
    return '';
},

getCurrentRegionFields: function () {},

calcHighlightColor: function (color, options) {
    var highlightColor = options.get('highlightColor'),
        lighten = options.get('highlightLighten'),
        parse, mult, rgbnew, i;
    if (highlightColor) {
        return highlightColor;
    }
    if (lighten) {
        // extract RGB values
        parse = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(color) || /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(color);
        if (parse) {
            rgbnew = [];
            mult = color.length === 4 ? 16 : 1;
            for (i = 0; i < 3; i++) {
                rgbnew[i] = clipval(Math.round(parseInt(parse[i + 1], 16) * mult * lighten), 0, 255);
            }
            return 'rgb(' + rgbnew.join(',') + ')';
        }

    }
    return color;
}

});

barHighlightMixin = {

changeHighlight: function (highlight) {
    var currentRegion = this.currentRegion,
        target = this.target,
        shapeids = this.regionShapes[currentRegion],
        newShapes;
    // will be null if the region value was null
    if (shapeids) {
        newShapes = this.renderRegion(currentRegion, highlight);
        if ($.isArray(newShapes) || $.isArray(shapeids)) {
            target.replaceWithShapes(shapeids, newShapes);
            this.regionShapes[currentRegion] = $.map(newShapes, function (newShape) {
                return newShape.id;
            });
        } else {
            target.replaceWithShape(shapeids, newShapes);
            this.regionShapes[currentRegion] = newShapes.id;
        }
    }
},

render: function () {
    var values = this.values,
        target = this.target,
        regionShapes = this.regionShapes,
        shapes, ids, i, j;

    if (!this.cls._super.render.call(this)) {
        return;
    }
    for (i = values.length; i--;) {
        shapes = this.renderRegion(i);
        if (shapes) {
            if ($.isArray(shapes)) {
                ids = [];
                for (j = shapes.length; j--;) {
                    shapes[j].append();
                    ids.push(shapes[j].id);
                }
                regionShapes[i] = ids;
            } else {
                shapes.append();
                regionShapes[i] = shapes.id; // store just the shapeid
            }
        } else {
            // null value
            regionShapes[i] = null;
        }
    }
    target.render();
}

};