/* ------------------------------------------------------ */
/*
 * This file includes the dvtm:areaChart tag processor
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

// ------ jQuery Mobile Renderer ------ //
(function() {
    /**
     * - first param is the namespace.
     * - second param is a js object where each property name matches the tag name to be rendered
     */
    amx.registerRenderers("dvtm",{

        areaChart: {
            create: function(amxNode) {

                dvtm.initChartAmxNode(amxNode);
                dvtm.setChartType(amxNode, "area");
                dvtm.setCommonChartProperties(amxNode);

                var $content = dvtm.setupChartDiv(amxNode);

                // if no 'value' and stamps defined, assume the data was
                // set using the 'data' attribute and exit
                if (!amx.dtmode && (amxNode.value == undefined)) {
                    return $content;
                }

                return dvtm.processChildTags(amxNode).pipe(function() {
                    return $content;
                });
            },

            init: function($node, amxNode) {

                dvtm.initChartDomNode($node, amxNode);
                dvtm.setCssBasedProperties($node, amxNode);
            },

            postDisplay: function($node, amxNode) {
                dvtm.renderChart($node, amxNode);
            },

            refresh: function($node, newAmxNode, changedProperties) {
                var oldAmxNode = $node.data("amxNode");

                dvtm.initChartAmxNodeForRefresh(oldAmxNode, newAmxNode);
                dvtm.setCommonChartProperties(newAmxNode);
                
                return dvtm.processChildTags(newAmxNode).pipe(function() {
                    dvtm.renderChart($node, newAmxNode);
                });
            }

        }

    });
})();


/* ------------------------------------------------------ */
/*
 * This file includes the dvtm:barChart tag processor
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

// ------ jQuery Mobile Renderer ------ //
(function() {
    /**
     * - first param is the namespace.
     * - second param is a js object where each property name matches the tag name to be rendered
     */
    amx.registerRenderers("dvtm",{

        barChart: {
            create: function(amxNode) {

                dvtm.initChartAmxNode(amxNode);
                dvtm.setChartType(amxNode, "bar");
                dvtm.setCommonChartProperties(amxNode);

                var $content = dvtm.setupChartDiv(amxNode);

                // if no 'value' and stamps defined, assume the data was
                // set using the 'data' attribute and exit
                if (!amx.dtmode && (amxNode.value == undefined)) {
                    return $content;
                }

                return dvtm.processChildTags(amxNode).pipe(function() {
                    return $content;
                });

            },

            init: function($node, amxNode) {

                dvtm.initChartDomNode($node, amxNode);
                dvtm.setCssBasedProperties($node, amxNode);
            },

            postDisplay: function($node, amxNode) {
                dvtm.renderChart($node, amxNode);
            },

            refresh: function($node, newAmxNode, changedProperties) {
                var oldAmxNode = $node.data("amxNode");

                dvtm.initChartAmxNodeForRefresh(oldAmxNode, newAmxNode);
                dvtm.setCommonChartProperties(newAmxNode);

                return dvtm.processChildTags(newAmxNode).pipe(function() {
                    dvtm.renderChart($node, newAmxNode);
                });
            }
        }

    });
})();


/* ------------------------------------------------------ */
/*
 * This file includes the dvtm:bubbleChart tag processor
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

// ------ jQuery Mobile Renderer ------ //
(function() {
    /**
     * - first param is the namespace.
     * - second param is a js object where each property name matches the tag name to be rendered
     */
    amx.registerRenderers("dvtm",{

        bubbleChart: {
            create: function(amxNode) {

                dvtm.initChartAmxNode(amxNode);
                dvtm.setChartType(amxNode, "bubble");
                dvtm.setCommonChartProperties(amxNode);

                var $content = dvtm.setupChartDiv(amxNode);

                // if no 'value' and stamps defined, assume the data was
                // set using the 'data' attribute and exit
                if (!amx.dtmode && (amxNode.value == undefined)) {
                    return $content;
                }

                return dvtm.processChildTags(amxNode).pipe(function(){
                    return $content;
                });

            },

            init: function($node, amxNode) {

                dvtm.initChartDomNode($node, amxNode);
                dvtm.setCssBasedProperties($node, amxNode);
            },

            postDisplay: function($node, amxNode) {
                dvtm.renderChart($node, amxNode);
            },

            refresh: function($node, newAmxNode, changedProperties) {
                var oldAmxNode = $node.data("amxNode");

                dvtm.initChartAmxNodeForRefresh(oldAmxNode, newAmxNode);
                dvtm.setCommonChartProperties(newAmxNode);
                
                return dvtm.processChildTags(newAmxNode).pipe(function() {
                    dvtm.renderChart($node, newAmxNode);
                });
            }
        }
    });
})();


/* ------------------------------------------------------ */
/*
 * This file includes the dvtm:barChart tag processor
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

// ------ jQuery Mobile Renderer ------ //
(function() {
    /**
     * - first param is the namespace.
     * - second param is a js object where each property name matches the tag name to be rendered
     */
    amx.registerRenderers("dvtm",{

        comboChart: {
            create: function(amxNode) {

                dvtm.initChartAmxNode(amxNode);
                dvtm.setChartType(amxNode, "combo");
                dvtm.setCommonChartProperties(amxNode);

                var $content = dvtm.setupChartDiv(amxNode);

                // if no 'value' and stamps defined, assume the data was
                // set using the 'data' attribute and exit
                if (!amx.dtmode && (amxNode.value == undefined)) {
                    return $content;
                }

                return dvtm.processChildTags(amxNode).pipe(function() {
                    return $content;
                });
            },

            init: function($node, amxNode) {

                dvtm.initChartDomNode($node, amxNode);
                dvtm.setCssBasedProperties($node, amxNode);
            },

            postDisplay: function($node, amxNode) {
                dvtm.renderChart($node, amxNode);
            },

            refresh: function($node, newAmxNode, changedProperties) {
                var oldAmxNode = $node.data("amxNode");

                dvtm.initChartAmxNodeForRefresh(oldAmxNode, newAmxNode);
                dvtm.setCommonChartProperties(newAmxNode);

                return dvtm.processChildTags(amxNode).pipe(function() {
                    dvtm.renderChart($node, newAmxNode);
                });
            }
        }
    });
})();


/* ------------------------------------------------------ */
/*
 * This file includes the common tag processing methods for
 * the dvtm tag renderers.
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

// ------ jQuery Mobile Renderer ------ //
(function() {

    dvtm.DEFAULT_AREA_DATA = {
        groups: ["Group A", "Group B", "Group C", "Group D"],
        series: [{name : "Series 1", data : [74, 42, 70, 46]},
                 {name : "Series 2", data : [50, 58, 46, 54]},
                 {name : "Series 3", data : [34, 22, 30, 32]},
                 {name : "Series 4", data : [18,  6, 14, 22]}]
    };

    dvtm.DEFAULT_BAR_DATA = {
        groups: ["Group A", "Group B"],
        series: [{name : "Series 1", data : [42, 34]},
                 {name : "Series 2", data : [55, 30]},
                 {name : "Series 3", data : [36, 50]},
                 {name : "Series 4", data : [22, 46]},
                 {name : "Series 5", data : [22, 46]}]
    };

    dvtm.DEFAULT_LINE_DATA = {
        groups: ["Group A", "Group B", "Group C", "Group D", "Group E"],
        series: [{name : "Series 1", data : [74, 62, 70, 76, 66]},
                 {name : "Series 2", data : [50, 38, 46, 54, 42]},
                 {name : "Series 3", data : [34, 22, 30, 32, 26]},
                 {name : "Series 4", data : [18,  6, 14, 22, 10]},
                 {name : "Series 5", data : [3,  2,  3,  3,  2]}]
    };

    dvtm.DEFAULT_BUBBLE_DATA = {
        groups: ["Group A", "Group B", "Group C"],
        series: [{name : "Series 1", data : [{x:15, y:25, z:5}, {x:25, y:30, z:12}, {x:25, y:45, z:12}]},
                 {name : "Series 2", data : [{x:15, y:15, z:8}, {x:20, y:35, z:14}, {x:40, y:55, z:35}]},
                 {name : "Series 3", data : [{x:10, y:10, z:8}, {x:18, y:55, z:10}, {x:40, y:50, z:18}]},
                 {name : "Series 4", data : [{x:8, y:20, z:6}, {x:11, y:30,  z:8}, {x:30, y:40, z:15}]}]
    };

    dvtm.DEFAULT_SCATTER_DATA = {
        groups: ["Group A", "Group B", "Group C"],
        series: [{name : "Series 1", data : [{x:15, y:15}, {x:25, y:43}, {x:25, y:25}]},
                 {name : "Series 2", data : [{x:25, y:15}, {x:55, y:45}, {x:57, y:47}]},
                 {name : "Series 3", data : [{x:17, y:36}, {x:32, y:52}, {x:26, y:28}]},
                 {name : "Series 4", data : [{x:38, y:22}, {x:43, y:43}, {x:58, y:36}]}]
    };

    dvtm.DEFAULT_PIE_DATA = dvtm.DEFAULT_BAR_DATA;

    dvtm.DEFAULT_COMBO_DATA = dvtm.DEFAULT_BAR_DATA;


    /**
     * processes the legend tag if present.
     */
    dvtm.processLegendTag = function(amxNode) {

        var dfd = $.Deferred();

        var xmlLegend = $(amxNode._xmlNode).children("dvtm\\:legend")[0];

        if (xmlLegend) {
            return amx.processXmlNode(xmlLegend).done(function(legend) {
                dvtm.setLegendProperties(amxNode, legend);

                // process legend sections if any
                var xmlLegendSections = $(xmlLegend).children("dvtm\\:legendSection");
                return amx.serialResolve(xmlLegendSections, function(xmlLegendSection, index) {
                    return amx.processXmlNode(xmlLegendSection).done(function(legendSectionNode) {
                        dvtm.processLegendSection(amxNode, legendSectionNode);
                    });
                });

            });
        }
        else {
            dfd.resolve();
            return dfd.promise();
        }
    }

    /**
     * processes the axis tag
     *
     * @param amxNode  the current amxNode
     * @param axisId   the axis name (xAxis, yAxis, or y2Axis)
     */
    dvtm.processAxisTag = function(amxNode, axisId) {

        var dfd;
        var defers = [];

        var xmlAxis = $(amxNode._xmlNode).children("dvtm\\:" + axisId)[0];

        if (xmlAxis) {
            return amx.processXmlNode(xmlAxis).done(function(axis) {
                dvtm.setAxisProperties(amxNode, axis, axisId);

                // process referenceObjects if any
                var xmlReferenceObjs = $(xmlAxis).children("dvtm\\:referenceObject");
                dfd = amx.serialResolve(xmlReferenceObjs, function(xmlReferenceObj, index) {
                    return amx.processXmlNode(xmlReferenceObj).done(function(referenceObjNode) {
                        dvtm.processReferenceObj(amxNode, referenceObjNode, axisId);
                    });
                });

                defers.push(dfd);

                // process tickLabel if any
                var xmlTickLabel = $(xmlAxis).children("dvtm\\:tickLabel")[0];
                if (xmlTickLabel) {
                    dfd = amx.processXmlNode(xmlTickLabel).done(function(tickLabelNode) {
                        dvtm.processTickLabel(amxNode, tickLabelNode, axisId);
                    });
                    defers.push(dfd);
                }

                // process axisLine if any
                var xmlAxisLine = $(xmlAxis).children("dvtm\\:axisLine")[0];
                if (xmlAxisLine) {
                    dfd = amx.processXmlNode(xmlAxisLine).done(function(axisLineNode) {
                        dvtm.processAxisLine(amxNode, axisLineNode, axisId);
                    });
                    defers.push(dfd);
                }

                // process major tick tag, if any
                var xmlMajorTick = $(xmlAxis).children("dvtm\\:majorTick")[0];
                if (xmlMajorTick) {
                    dfd = amx.processXmlNode(xmlMajorTick).done(function(majorTickNode) {
                        dvtm.processTick(amxNode, majorTickNode, axisId, true);
                    });
                    defers.push(dfd);
                }

                // process minor tick tag, if any
                var xmlMinorTick = $(xmlAxis).children("dvtm\\:minorTick")[0];
                if (xmlMinorTick) {
                    dfd = amx.processXmlNode(xmlMinorTick).done(function(minorTickNode) {
                        dvtm.processTick(amxNode, minorTickNode, axisId, false);
                    });
                    defers.push(dfd);
                }

                $.when.apply(null, defers).done(function() {
                    var dfd2 = $.Deferred();
                    dfd2.resolve();
                    return dfd2.promise();
                })
            });
        }
        else {
            dfd = $.Deferred();
            dfd.resolve();
            return dfd.promise();
        }
    }

    /**
     * processes the format tag
     *
     * @param amxNode  the current amxNode
     * @param formatId   where to apply the format (xFormat, yFormat, y2Format, zFormat)
     */
    dvtm.processFormatTag = function(amxNode, formatId) {
        var dfd = $.Deferred();
        var xmlFormat = $(amxNode._xmlNode).children("dvtm\\:" + formatId)[0];
        var format;

        if (xmlFormat) {
          switch (formatId) {
            case 'xFormat':
                format = amxNode["_optionsObj"].styleDefaults.x1Format;
                break;
            case 'yFormat':
                format = amxNode["_optionsObj"].styleDefaults.y1Format;
                break;
            case 'y2Format':
                format = amxNode["_optionsObj"].styleDefaults.y2Format;
                break;
            case 'zFormat':
                format = amxNode["_optionsObj"].styleDefaults.zFormat;
                break;
            default:
                // unsupported!
                dfd.resolve();
                return dfd.promise();
          }
          return amx.processXmlNode(xmlFormat).done(function(formatNode) {
            if (formatNode.converter)
              format.converter = formatNode.converter;
          });
        }
        else {
            dfd.resolve();
            return dfd.promise();
        }
    }

    /**
     * processSimpleChildTags processes non-data child tags such as legend, axes and formats
     */
    dvtm.processSimpleChildTags = function(amxNode) {

        var defers = [];
        var dfd;

        dfd = dvtm.processLegendTag(amxNode);
        defers.push(dfd);
        dfd = dvtm.processAxisTag(amxNode, 'xAxis');
        defers.push(dfd);
        dfd = dvtm.processAxisTag(amxNode, 'yAxis');
        defers.push(dfd);
        dfd = dvtm.processAxisTag(amxNode, 'y2Axis');
        defers.push(dfd);
        dfd = dvtm.processFormatTag(amxNode, 'xFormat');
        defers.push(dfd);
        dfd = dvtm.processFormatTag(amxNode, 'yFormat');
        defers.push(dfd);
        dfd = dvtm.processFormatTag(amxNode, 'y2Format');
        defers.push(dfd);
        dfd = dvtm.processFormatTag(amxNode, 'zFormat');
        defers.push(dfd);

        return $.when.apply(null, defers).done(function() {
            dfd = $.Deferred();
            dfd.resolve();
            return dfd;
        });
    }

    /**
     * processes the dataStamp facet
     */
    dvtm.processDataStamp = function(amxNode) {

        var dfd = $.Deferred();
        var varName = amxNode["var"]; // need to use this since var is reserved

        if (amxNode.facets.dataStamp == undefined) {
            dfd.resolve();
            return dfd.promise();
        }

        var xmlMarkerCol = $(amxNode.facets.dataStamp._xmlNode).children("dvtm\\:chartDataItem");

        // amxNode.value is the array of "stamps"
        return amx.serialResolve(amxNode.value, function(stamp) {
            // we add the stamp
            adf.el.addVariable(varName, stamp);

            return amx.serialResolve(xmlMarkerCol, function(xmlMarker, idx) {
                // we process the xmlMarker xml with this stamp
                return amx.processXmlNode(xmlMarker).done(function(markerNode) {
                    // do what you need to do per item here

                    // process marker attributes
                    var marker = dvtm.processMarker(amxNode, markerNode, stamp.getProvider());

                    if (marker != null) {
                        // process attribute groups, if any
                        var xmlAttributeGroups = $(xmlMarker).children("dvtm\\:attributeGroups");

                        return amx.serialResolve(xmlAttributeGroups, function(xmlAttrGrpItem, index) {

                            return amx.processXmlNode(xmlAttrGrpItem).done(function(attrGroupsNode) {
                                dvtm.processAttributeGroup(amxNode, marker, attrGroupsNode, index);
                            });
                        });
                    }
                    else {
                        var dfd2 = $.Deferred();
                        dfd2.resolve();
                        return dfd2.promise();
                    }
                });
            }).done(function() {
                //make sure to clean the variable
                adf.el.removeVariable(varName);
            });
        });
    }

    /*
     * processes the seriesStamp facet
     */
    dvtm.processSeriesStamp = function(amxNode) {

        var dfd = $.Deferred();
        var varName = amxNode["var"];
        var chartType = dvtm.getChartType(amxNode);

        if ((chartType === 'area' || chartType === 'bar' || chartType === 'combo'
               || chartType === 'horizontalBar' || chartType === 'line')
               && (amxNode.facets.seriesStamp && varName !== undefined)) {

            var seriesStampCol = amxNode.value;
            // we are going to iterate the same collection that was used in dataStamp
            // need to rewind to index 0
            if (seriesStampCol && seriesStampCol.setCurrentIndex) {
                seriesStampCol.setCurrentIndex(0);
            }
            var xmlSeriesStyleCol = $(amxNode.facets.seriesStamp._xmlNode).children("dvtm\\:seriesStyle");

            return amx.serialResolve(seriesStampCol, function(seriesStamp) {
                // we add the stamp
                adf.el.addVariable(varName, seriesStamp);

                if (xmlSeriesStyleCol.length > 0) {
                    return amx.serialResolve(xmlSeriesStyleCol, function(xmlSeriesStyle, idx) {
                        return amx.processXmlNode(xmlSeriesStyle).done(function (seriesStyleNode) {
                            dvtm.processSeriesStyle(amxNode, seriesStyleNode);
                        });
                    });
                }
                else {
                    var dfd2 = $.Deferred();
                    dfd2.resolve();
                    return dfd2.promise();
                }
                adf.el.removeVariable(varName);
            });
        }
        else {
            dfd = $.Deferred();
            dfd.resolve();
            return dfd.promise();
        }
    }

    /**
     * processes the chart's child tags
     */
    dvtm.processChildTags = function(amxNode) {

        var chartType = dvtm.getChartType(amxNode);
        var data = amxNode["_dataObj"];

        // in DT mode, just set the static data, process legend, axis, 
        // and format tags and return
        if (amx.dtmode) {

            switch (chartType) {
                case 'area':
                    data.groups = dvtm.DEFAULT_AREA_DATA.groups;
                    data.series = dvtm.DEFAULT_AREA_DATA.series;
                    break;
                case 'bar':
                    data.groups = dvtm.DEFAULT_BAR_DATA.groups;
                    data.series = dvtm.DEFAULT_BAR_DATA.series;
                    break;
                case 'bubble':
                    data.groups = dvtm.DEFAULT_BUBBLE_DATA.groups;
                    data.series = dvtm.DEFAULT_BUBBLE_DATA.series;
                    break;
                case 'combo':
                    data.groups = dvtm.DEFAULT_COMBO_DATA.groups;
                    data.series = dvtm.DEFAULT_COMBO_DATA.series;
                    break;
                case 'horizontalBar':
                    data.groups = dvtm.DEFAULT_BAR_DATA.groups;
                    data.series = dvtm.DEFAULT_BAR_DATA.series;
                    break;
                case 'line':
                    data.groups = dvtm.DEFAULT_LINE_DATA.groups;
                    data.series = dvtm.DEFAULT_LINE_DATA.series;
                    break;
                case 'scatter':
                    data.groups = dvtm.DEFAULT_SCATTER_DATA.groups;
                    data.series = dvtm.DEFAULT_SCATTER_DATA.series;
                    break;
                default:
                    // no DT support for other chart types
                    break;
            }

            return dvtm.processSimpleChildTags(amxNode);
        }

        // RT mode only

        return dvtm.processDataStamp(amxNode).done(function() {
            //Do everything you want to be done after all the stamping here
            dvtm.applyMarkersToModel(amxNode);

            return dvtm.processSeriesStamp(amxNode).done(function() {
                return dvtm.processSimpleChildTags(amxNode);
            });

        }).pipe(function() {
//                return $content;
        })

    }


    /**
     * Processes pieChart child tags
     */
    dvtm.processPieChildTags = function(amxNode) {

        var defers = [];
        var dfd;
        var options = amxNode["_optionsObj"];
        var data = amxNode["_dataObj"];
        var varName = amxNode["var"]; // need to use this since var is reserved

        // resolve pieChart specific properties here
        if (amxNode.threeDEffect !== options.styleDefaults.threeDEffect) {
            options.styleDefaults.threeDEffect = amxNode.threeDEffect;
            dvtm.setOptionsDirty(amxNode, true);
        }
        
        // process sliceLabel if any
        var xmlSliceLabel = $(amxNode._xmlNode).children("dvtm\\:sliceLabel")[0];
        if (xmlSliceLabel) {
            dfd = amx.processXmlNode(xmlSliceLabel).done(function(sliceLabelNode) {
                dvtm.processSliceLabel(amxNode, sliceLabelNode);
            });
            defers.push(dfd);
        }
        
        // process pieValueFormat if any
        var xmlPieValueFormat = $(amxNode._xmlNode).children("dvtm\\:pieValueFormat")[0];
        if (xmlPieValueFormat) {
            dfd = amx.processXmlNode(xmlPieValueFormat).done(function(pieValueFormatNode) {
                dvtm.processPieValueFormat(amxNode, pieValueFormatNode);
            });
            defers.push(dfd);
        }

        // this is the XMLTag that we will use for stamping
        var xmlLegend = $(amxNode._xmlNode).children("dvtm\\:legend")[0];

        // first process tht non-stamped elements
        if (xmlLegend) {
            dfd = amx.processXmlNode(xmlLegend).done(function(legend) {
                dvtm.setLegendProperties(amxNode, legend);
            });
            defers.push(dfd);
        }

        if (amx.dtmode) {
            // in DT mode, do not try to resolve data item values,
            // just set the default static data and return
            data.groups = dvtm.DEFAULT_BAR_DATA.groups;
            data.series = dvtm.DEFAULT_BAR_DATA.series;
        }
        else if (amxNode.value !== undefined && amxNode.facets.dataStamp) {

            // currently, pie chart has no real groups, set one empty group
            data.groups.push("");

            // in RT mode, process the data stamp facet
            var xmlPieSlice = $(amxNode.facets.dataStamp._xmlNode).children("dvtm\\:pieDataItem")[0];

            // amxNode.value is the array of "stamps"
            dfd = amx.serialResolve(amxNode.value, function(stamp) {
                // we add the stamp
                adf.el.addVariable(varName, stamp);

                // we process the xmlPieSlice xml with this stamp
                return amx.processXmlNode(xmlPieSlice).done(function(pieSlice) {
                    // do what you need to do per item here
                    // first check, if the data item should be rendered at all
                    if ((pieSlice.rendered == undefined) || amx.isValueTrue(pieSlice.rendered)) {
                        // resolved the slice id
                        var sliceId;
                        if (pieSlice.sliceId != undefined) {
                            sliceId = pieSlice.sliceId;
                        }
                        else {
                            sliceId = pieSlice.label;
                        }

                        var slice = {id: sliceId, name: pieSlice.label, data: [ +pieSlice.value ]};

                        if (pieSlice.explode != undefined) {
                            slice.pieSliceExplode = pieSlice.explode;
                        }
                        dvtm.addSeriesItem(amxNode, slice);
                    }
                });
            }).done(function(){
                //make sure to clean the variable
                adf.el.removeVariable(varName);
            });

            defers.push(dfd);
        }
        return $.when.apply(null, defers).done(function() {
            var dfd2 = $.Deferred();
            dfd2.resolve();
            return dfd2.promise();
        });
    }


    /**
     * Processes sparkChart child tags
     */
    dvtm.processSparkChildTags = function(amxNode) {

        var defers = [];
        var dfd;
        var varName = amxNode["var"]; // need to use this since var is reserved

        // if no 'value' and stamps defined, assume the data was
        // set using the 'data' attribute and exit
        if (amxNode.value == undefined) {
            dfd = $.Deferred();
            dfd.resolve();
            return dfd.promise();
        }

        // process referenceObjects if any
        var xmlReferenceObjs = $(amxNode._xmlNode).children("dvtm\\:referenceObject");
        dfd = amx.serialResolve(xmlReferenceObjs, function(xmlReferenceObj, index) {
            return amx.processXmlNode(xmlReferenceObj).done(function(referenceObjNode) {
                dvtm.processReferenceObj(amxNode, referenceObjNode, 'spark');
            });
        });
        defers.push(dfd);

        if (amxNode.facets.dataStamp) {
            var xmlSparkItem = $(amxNode.facets.dataStamp._xmlNode).children("dvtm\\:sparkDataItem")[0];

            // amxNode.value is the array of "stamps"
            dfd = amx.serialResolve(amxNode.value, function(stamp) {
                // we add the stamp
                adf.el.addVariable(varName, stamp);

                // we process the xmlSparkItem xml with this stamp
                return amx.processXmlNode(xmlSparkItem).done(function(sparkItem) {
                    dvtm.processSparkDataItem(amxNode, sparkItem);
                });
            }).done(function(){
                //make sure to clean the variable
                adf.el.removeVariable(varName);
            });
            defers.push(dfd);
        }

        return $.when.apply(null, defers).done(function() {
            var dfd2 = $.Deferred();
            dfd2.resolve();
            return dfd2.promise();
        });
    }

    /**
     * Process gauge child tags
     */
    dvtm.processGaugeChildTags = function(amxNode) {
        // process threshold if any
        var xmlThresholds = $(amxNode._xmlNode).children("dvtm\\:threshold");
        return amx.serialResolve(xmlThresholds, function(xmlThreshold, index) {
            return amx.processXmlNode(xmlThreshold).done(function(thresholdNode) {
                dvtm.processThreshold(amxNode, thresholdNode);
            });
        });
    }

})();

/* ------------------------------------------------------ */
/*
 * This file includes the dvtm renderers
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

// ------ jQuery Mobile Renderer ------ //
(function() {
    /**
	 * - first param is the namespace.
	 * - second param is a js object where each property name matches the tag name to be rendered
	 */
    amx.registerRenderers("dvtm",{
    
        sourceViewer: {
            create: function(amxNode) {
                var $textArea = $("<textarea id=\"viewer\" readonly=\"true\" wrap=\"off\" cols=\"60\" rows=\"30\" style=\"width: 100%; height: 100%;\"></textarea>");
                
                if (amxNode.src) {
                    $.get(amxNode.src, function(data) {
                        var pattern = /\s*((<dvtm:(area|bar|bubble|combo|horizontalBar|line|pie|scatter|spark)Chart[\s\S]*?Chart>)|(<dvtm:(led|statusMeter)Gauge[\s\S]*Gauge>))/m;

                        var text = pattern.exec(data);

                        if (!text || text.length <= 0)
                            text = data;
                        else
                            text = text[0];

                        $textArea.val(text);
                    }, 'text');
                }
                return $textArea;
            },

            init: function($node, amxNode) {
		setTimeout( function() {
                    // remove some nasty jquery mobile styles
                    $node.removeClass("ui-input-text");
                    // $node.removeClass("ui-shadow-inset");
                    $node.removeClass("ui-body-b");
                }, 400);

            }

        },

        chart: {
            
            // THIS IS THE OLD STYLE! SHOULDN'T BE USED ANYMORE!
            
            create: function(amxNode) {
                var $content = $("<div style=\"width: 100%; height: 100%;\"></div>");

                return $content;
            },

            init: function($node, amxNode) {

                var dataObj = DvtJSONUtils.clone(ChartData.BAR_DATA);
                var optionsObj = {
                    chart: {type: "bar"},
                    titleSeparator: {rendered: false},
                    styleDefaults: {threeDEffect: "off"}
                };

                if (amxNode.type) {
                    optionsObj.chart.type = amxNode.type;
                    switch (amxNode.type) {
                        case "area":
                            dataObj = DvtJSONUtils.clone(ChartData.AREA_DATA);
                            dataObj.title = "Area Chart";
                            break;
                        case "bar":
                            dataObj = DvtJSONUtils.clone(ChartData.BAR_DATA);
                            dataObj.title = "Bar Chart";
                            break;
                        case "line":
                            dataObj = DvtJSONUtils.clone(ChartData.LINE_DATA);
                            dataObj.title = "Line Chart";
                            break;
                        case "scatter":
                            dataObj = DvtJSONUtils.clone(ChartData.SCATTER_DATA);
                            dataObj.title = "Scatter Chart";
                            break;
                    }
                }

                // if 'data' attribute was defined, override the default
                if (amxNode.data) {
                    dataObj = amxNode.data;
                }

                // Create the SVG Element
                var svg = DvtSvgUtils.createSvgDocument();

                var temp = document.createElement("div");
                document.body.appendChild(temp);
                temp.appendChild(svg);

                // Create the component and pass in the rendering context
                var context = new DvtSvgContext(svg);
                var chart = DvtChart.newInstance(context, null, null, optionsObj);
                context.getStage().addChild(chart);
                chart.render(dataObj, 300, 250);
                document.body.removeChild(temp);
                temp.removeChild(svg);

                $node.append(svg);

            }
        }
    });
    
})();

/* ------------------------------------------------------ */
/*
 * This file includes the dvtm:barChart tag processor
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

// ------ jQuery Mobile Renderer ------ //
(function() {
    /**
     * - first param is the namespace.
     * - second param is a js object where each property name matches the tag name to be rendered
     */
    amx.registerRenderers("dvtm",{

        horizontalBarChart: {
            create: function(amxNode) {

                dvtm.initChartAmxNode(amxNode);
                dvtm.setChartType(amxNode, "horizontalBar");
                dvtm.setCommonChartProperties(amxNode);

                var $content = dvtm.setupChartDiv(amxNode);

                // if no 'value' and stamps defined, assume the data was
                // set using the 'data' attribute and exit
                if (!amx.dtmode && (amxNode.value == undefined)) {
                    return $content;
                }

                return dvtm.processChildTags(amxNode).pipe(function() {
                    return $content;
                });
            },

            init: function($node, amxNode) {

                dvtm.initChartDomNode($node, amxNode);
                dvtm.setCssBasedProperties($node, amxNode);
            },

            postDisplay: function($node, amxNode) {
                dvtm.renderChart($node, amxNode);
            },

            refresh: function($node, newAmxNode, changedProperties) {
                var oldAmxNode = $node.data("amxNode");

                dvtm.initChartAmxNodeForRefresh(oldAmxNode, newAmxNode);
                dvtm.setCommonChartProperties(newAmxNode);

                return dvtm.processChildTags(amxNode).pipe(function() {
                    dvtm.renderChart($node, newAmxNode);
                });
            }

        }

    });
})();


/* ------------------------------------------------------ */
/*
 * This file includes the dvtm:ledGauge tag processor
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

// ------ jQuery Mobile Renderer ------ //
(function() {
    /**
     * - first param is the namespace.
     * - second param is a js object where each property name matches the tag name to be rendered
     */
    amx.registerRenderers("dvtm",{

        ledGauge: {
            create: function(amxNode) {

                dvtm.initGaugeAmxNode(amxNode);
                dvtm.setCommonGaugeProperties(amxNode);

                var $content = dvtm.setupChartDiv(amxNode);

                // if no 'value' and stamps defined, assume the data was
                // set using the 'data' attribute and exit
                if (amxNode.value == undefined) {
                    return $content;
                }

                return dvtm.processGaugeChildTags(amxNode).pipe(function(){
                    return $content;
                });
            },

            init: function($node, amxNode) {

                dvtm.initChartDomNode($node, amxNode);
                dvtm.setCssBasedProperties($node, amxNode);
            },

            postDisplay: function($node, amxNode) {
                dvtm.renderChart($node, amxNode);
            },

            refresh: function($node, newAmxNode, changedProperties) {
                var oldAmxNode = $node.data("amxNode");

                dvtm.initGaugeAmxNodeForRefresh(oldAmxNode, newAmxNode);
                dvtm.setCommonGaugeProperties(newAmxNode);

                return dvtm.processGaugeChildTags(newAmxNode).pipe(function() {
                    dvtm.renderChart($node, newAmxNode);
                });
            }
        }

    });
})();


/* ------------------------------------------------------ */
/*
 * This file includes the dvtm:barChart tag processor
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

// ------ jQuery Mobile Renderer ------ //
(function() {
    /**
     * - first param is the namespace.
     * - second param is a js object where each property name matches the tag name to be rendered
     */
    amx.registerRenderers("dvtm",{

        lineChart: {
            create: function(amxNode) {

                dvtm.initChartAmxNode(amxNode);
                dvtm.setChartType(amxNode, "line");
                dvtm.setCommonChartProperties(amxNode);

                var $content = dvtm.setupChartDiv(amxNode);

                // if no 'value' and stamps defined, assume the data was
                // set using the 'data' attribute and exit
                if (!amx.dtmode && (amxNode.value == undefined)) {
                    return $content;
                }

                return dvtm.processChildTags(amxNode).pipe(function() {
                    return $content;
                });
            },

            init: function($node, amxNode) {

                dvtm.initChartDomNode($node, amxNode);
                dvtm.setCssBasedProperties($node, amxNode);
            },

            postDisplay: function($node, amxNode) {
                dvtm.renderChart($node, amxNode);
            },

            refresh: function($node, newAmxNode, changedProperties) {
                var oldAmxNode = $node.data("amxNode");

                dvtm.initChartAmxNodeForRefresh(oldAmxNode, newAmxNode);
                dvtm.setCommonChartProperties(newAmxNode);

                return dvtm.processChildTags(amxNode).pipe(function() {
                    dvtm.renderChart($node, newAmxNode);
                });
            }

        }
    });
})();


/* ------------------------------------------------------ */
/*
 * This file includes the dvtm:pieChart tag processor
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

// ------ jQuery Mobile Renderer ------ //
(function() {
    /**
	 * - first param is the namespace.
	 * - second param is a js object where each property name matches the tag name to be rendered
	 */
    amx.registerRenderers("dvtm",{

        pieChart: {
            create: function(amxNode) {

                dvtm.initChartAmxNode(amxNode);
                dvtm.setChartType(amxNode, "pie");
                dvtm.setCommonChartProperties(amxNode);

                var $content = dvtm.setupChartDiv(amxNode);

                return dvtm.processPieChildTags(amxNode).pipe(function() {
                    return $content;
                });

            },

            init: function($node, amxNode) {

                dvtm.initChartDomNode($node, amxNode);
                dvtm.setCssBasedProperties($node, amxNode);
            },

            postDisplay: function($node, amxNode) {
                dvtm.renderChart($node, amxNode);
            },

            refresh: function($node, newAmxNode, changedProperties) {
                var oldAmxNode = $node.data("amxNode");

                dvtm.initChartAmxNodeForRefresh(oldAmxNode, newAmxNode);
                dvtm.setCommonChartProperties(newAmxNode);
                
                return dvtm.processPieChildTags(newAmxNode).pipe(function() {
                    dvtm.renderChart($node, newAmxNode);
                });
            }

        }
    });
})();


/* ------------------------------------------------------ */
/*
 * This file includes the dvtm:bubbleChart tag processor
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

// ------ jQuery Mobile Renderer ------ //
(function() {
    /**
     * - first param is the namespace.
     * - second param is a js object where each property name matches the tag name to be rendered
     */
    amx.registerRenderers("dvtm",{

        scatterChart: {
            create: function(amxNode) {

                dvtm.initChartAmxNode(amxNode);
                dvtm.setChartType(amxNode, "scatter");
                dvtm.setCommonChartProperties(amxNode);

                var $content = dvtm.setupChartDiv(amxNode);

                // if no 'value' and stamps defined, assume the data was
                // set using the 'data' attribute and exit
                if (!amx.dtmode && (amxNode.value == undefined)) {
                    return $content;
                }

                return dvtm.processChildTags(amxNode).pipe(function() {
                    return $content;
                });
            },

            init: function($node, amxNode) {

                dvtm.initChartDomNode($node, amxNode);
                dvtm.setCssBasedProperties($node, amxNode);
            },

            postDisplay: function($node, amxNode) {
                dvtm.renderChart($node, amxNode);
            },

            refresh: function($node, newAmxNode, changedProperties) {
                var oldAmxNode = $node.data("amxNode");

                dvtm.initChartAmxNodeForRefresh(oldAmxNode, newAmxNode);
                dvtm.setCommonChartProperties(newAmxNode);

                return dvtm.processChildTags(amxNode).pipe(function() {
                    dvtm.renderChart($node, newAmxNode);
                });
            }

        }
    });
})();


/* ------------------------------------------------------ */
/*
 * This file includes the dvtm:sparkChart tag processor
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

// ------ jQuery Mobile Renderer ------ //
(function() {
    /**
     * - first param is the namespace.
     * - second param is a js object where each property name matches the tag name to be rendered
     */
    amx.registerRenderers("dvtm",{

        sparkChart: {
            create: function(amxNode) {

                dvtm.initChartAmxNode(amxNode);
                dvtm.setCommonChartProperties(amxNode);

                var $content = dvtm.setupChartDiv(amxNode);

                return dvtm.processSparkChildTags(amxNode).pipe(function() {
                    return $content;
                });
            },

            init: function($node, amxNode) {

                dvtm.initChartDomNode($node, amxNode);
                dvtm.setCssBasedProperties($node, amxNode);
            },

            postDisplay: function($node, amxNode) {
                dvtm.renderChart($node, amxNode);
            },

            refresh: function($node, newAmxNode, changedProperties) {
                var oldAmxNode = $node.data("amxNode");

                dvtm.initChartAmxNodeForRefresh(oldAmxNode, newAmxNode);
                dvtm.setCommonChartProperties(newAmxNode);

                return dvtm.processSparkChildTags(amxNode).pipe(function() {
                    dvtm.renderChart($node, newAmxNode);
                });
            }
        }

    });
})();


/* ------------------------------------------------------ */
/*
 * This file includes the dvtm:statusMeterGauge tag processor
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

// ------ jQuery Mobile Renderer ------ //
(function() {
    /**
     * - first param is the namespace.
     * - second param is a js object where each property name matches the tag name to be rendered
     */
    amx.registerRenderers("dvtm",{

        statusMeterGauge: {
            create: function(amxNode) {

                dvtm.initGaugeAmxNode(amxNode);
                dvtm.setCommonGaugeProperties(amxNode);

                var $content = dvtm.setupChartDiv(amxNode);

                // if no 'value' and stamps defined, assume the data was
                // set using the 'data' attribute and exit
                if (amxNode.value == undefined) {
                    return $content;
                }

                return dvtm.processGaugeChildTags(amxNode).pipe(function() {
                    return $content;
                });
            },

            init: function($node, amxNode) {

                dvtm.initChartDomNode($node, amxNode);
                dvtm.setCssBasedProperties($node, amxNode);
            },

            postDisplay: function($node, amxNode) {
                dvtm.renderChart($node, amxNode);
            },

            refresh: function($node, newAmxNode, changedProperties) {
                var oldAmxNode = $node.data("amxNode");

                dvtm.initGaugeAmxNodeForRefresh(oldAmxNode, newAmxNode);
                dvtm.setCommonGaugeProperties(newAmxNode);

                return dvtm.processGaugeChildTags(newAmxNode).pipe(function() {
                    dvtm.renderChart($node, newAmxNode);
                });
            }
        }

    });
})();


/* ------------------------------------------------------ */
/*
 * This file includes the utility methods for the dvtm module
 */
/* ------------------------------------------------------ */

var dvtm = dvtm || {};

dvtm.styleXml = null;

// ------ jQuery Mobile Renderer ------ //
(function() {

    dvtm.DEFAULT_CHART_WIDTH = 300;
    dvtm.DEFAULT_CHART_HEIGHT = 200;
    dvtm.DEFAULT_SPARK_CHART_WIDTH = 300;
    dvtm.DEFAULT_SPARK_CHART_HEIGHT = 100;
    dvtm.DEFAULT_LED_GAUGE_WIDTH = 100;
    dvtm.DEFAULT_LED_GAUGE_HEIGHT = 80;
    dvtm.DEFAULT_STATUS_METER_GAUGE_WIDTH = 300;
    dvtm.DEFAULT_STATUS_METER_GAUGE_HEIGHT = 40;

    dvtm.defaultShapes = [ 'circle', 'square', 'plus', 'diamond', 'triangleUp', 'triangleDown', 'human'];
    dvtm.defaultColors = ["#003366", "#CC3300", "#666699", "#006666", "#FF9900",
                         "#993366", "#99CC33", "#624390", "#669933", "#FFCC33",
                         "#006699", "#EBEA79"];
   
   // Utility function copied over from amx.js                      
   dvtm.splitLocale = function(locale) {
    var ret = [null,null,null];
    var start = 0;
    var end = locale.indexOf("_");
    if (end != -1){
      ret[0] = locale.substring(start, end);
    }else{
      ret[0] = locale;
      return ret;
    }
    start = ++end;
    end = locale.indexOf("_", start);
    if (end != -1){
      ret[1] = locale.substring(start, end);
    }else{
      ret[1] = locale.substring(start);
      return ret;
    }
    start = ++end;
    ret[2] = locale.substring(start);
    return ret;
  }
  
  // Utility function copied over from amx.js
  dvtm.generateLocaleList = function(locale, useVariant) {
    var localeJava = getJavaLanguage(locale); // will convert "-" to "_"
    var localeArray = dvtm.splitLocale(localeJava);
    var language = localeArray[0];
    var country = localeArray[1];
    var variant = localeArray[2];
    var localeList = [];
    if (locale.indexOf("en") != 0){
      localeList.push("en-US");
    }
    if (language != null){
      localeList.push(language);
      if (country != null){
        localeList.push(language+"-"+country);
        if (variant != null && useVariant){
          localeList.push(language+"-"+country+"-"+variant);
        }
      }
    }
    return localeList;
  }
  
  // load javascript with ajax call
  dvtm.ajaxLoadJavaScript = function(url, success, failure){
    $.ajax({
        url: url,
        success: function(js) {dvtm.RB = js;},
        error: failure,
        dataType: 'script',
        async: false
    }); 
  }
  
  dvtm.loadJavaScriptByLocaleImpl = function(localeList, constructor, predicate, callback){
    if (localeList.length == 0){
      callback(null);
      return;
    }
    var locale = localeList.pop();
    var url = constructor(locale);
    var failure = function(){
      dvtm.loadJavaScriptByLocaleImpl(localeList, constructor, predicate, callback);
    };
    var success = function(){
      if (predicate(locale)){
        callback(locale);
      }else{
        failure();
      }
    };
    dvtm.ajaxLoadJavaScript(url, success, failure);
  }
  
  dvtm.loadJavaScriptByLocale = function(localeList, constructor, predicate, callback){
    // clone the array before calling the load method since it will actually
    // modify the array as it searches
    var clonedList = $.merge([], localeList);
    dvtm.loadJavaScriptByLocaleImpl(clonedList, constructor, predicate, callback);
  }
    
  /**
   * Load DVT bundles based on user locale
   */
  dvtm.loadDvtResources = function(baseUrl) {
    var _locale = null;
    _locale = getUserLanguage();
    var localeList = dvtm.generateLocaleList(_locale, true);
        
    dvtm.loadDvtChartBundle(baseUrl, localeList, function(locale){
      if (locale === null){
        // do nothing, we can still fall back in chart API
      }
    }); 
  }
     
  /**
   * Load DVT chart bundles
   */
  dvtm.loadDvtChartBundle = function(baseUrl, localeList, callback) {
    var chartBundleUrlConstructor = function(locale){
        var url = baseUrl + "/resource/DvtChartBundle";
        if (locale.indexOf("en") == 0){
            return url + ".js";
        }
        return url + "_" + getJavaLanguage(locale) + ".js";
    }
    var chartBundleLoaded = function(locale){
        return typeof DvtChartBundle_RB !== "undefined";
    };
    dvtm.loadJavaScriptByLocale(localeList, chartBundleUrlConstructor, chartBundleLoaded, callback);
  }
  
    /**
     * Initializes the chart amxNode with the default dataObject and optionsObject.
     * Should be called as the first thing in tag:create.
     */
    dvtm.initChartAmxNode = function(amxNode) {
        
        // do not overwrite existing properties
        if (amxNode["_optionsObj"] == undefined) {
            amxNode["_optionsObj"] = {chart: {},
                                       titleSeparator: {rendered: false},
                                       title: {},
                                       subtitle: {},
                                       footnote: {},
                                       legend: {},
                                       xAxis:  {tickLabel: {}, axisLine: {}},
                                       yAxis:  {tickLabel: {}, axisLine: {}},
                                       y2Axis: {tickLabel: {}, axisLine: {}},
                                       styleDefaults: {sliceLabel: {}, x1Format: {}, y1Format: {}, y2Format: {}, zFormat: {}, pieValueFormat: {}}
                                     };
        }
        if (amxNode["_dataObj"] == undefined) {
            // if the data attribute is defined, use it to initialize the data object
            if (amxNode.data) {
                amxNode["_dataObj"] = amxNode.data;
            }
            else {
                if (amxNode.tagName === "dvtm:sparkChart") {
                  amxNode["_dataObj"] = {items: [], referenceObjects: []};
                } else {
                  amxNode["_dataObj"] = {series: [], groups: [], legend: {}};
                }
            }
        }
        if (amxNode["_attributeGroups"] == undefined) {
            amxNode["_attributeGroups"] = [];
        }
        if (amxNode["_markers"] == undefined) {
            amxNode["_markers"] = [];
        }

        // load the style xml file if present (only once)
        // must be synchronous
        if (!dvtm.styleXml) {
            $.ajax({
                url: "css/dvtm-style.xml",
                success: function(xml) {dvtm.styleXml = xml;},
                dataType: 'xml',
                async: false
            });
        }
        
        // load resource bundles
        if (!dvtm.RB) {
          dvtm.loadDvtResources("js/toolkit");
        }
            

    }

    /**
     * Initializes the chart amxNode on refresh by copying the old amxNode properties.
     * Should be called from tag:refresh.
     */
    dvtm.initChartAmxNodeForRefresh = function(oldAmxNode, newAmxNode) {

        // make a note that this is a refresh phase
        newAmxNode["_refreshing"] = true;

        // copy the reference to the previous chart object
        if (oldAmxNode._theChartObject != undefined) {
            newAmxNode._theChartObject = oldAmxNode._theChartObject;
        }

        // clear the 'dirty' flag on the options object
        dvtm.setOptionsDirty(newAmxNode, false);
        // optionsObject will be mostly reused
        newAmxNode["_optionsObj"] = oldAmxNode["_optionsObj"];

        // dataObject will be recreated from scratch
        if (newAmxNode.tagName === "dvtm:sparkChart") {
            newAmxNode["_dataObj"] = {items: [], referenceObjects: []};
        } else {
            newAmxNode["_dataObj"] = {series: [], groups: [], legend: {}};
        }

        if (newAmxNode["_attributeGroups"] == undefined) {
            newAmxNode["_attributeGroups"] = [];
        }
        if (newAmxNode["_markers"] == undefined) {
            newAmxNode["_markers"] = [];
        }

    }


    /**
     * Initializes the gauge amxNode with the default dataObject and optionsObject.
     * Should be called as the first thing in tag:create.
     */
    dvtm.initGaugeAmxNode = function(amxNode) {
        
        // do not overwrite existing properties
        if (amxNode["_optionsObj"] == undefined) {
            amxNode["_optionsObj"] = {legend: {}}
        }
        if (amxNode["_dataObj"] == undefined) {
            amxNode["_dataObj"] = {}; 
        }
        
        // load the style xml file if present (only once)
        // must be synchronous
        if (!dvtm.styleXml) {
            $.ajax({
                url: "css/dvtm-style.xml",
                success: function(xml) {dvtm.styleXml = xml;},
                dataType: 'xml',
                async: false
            });
        }
        
        // load resource bundles
        if (!dvtm.RB) {
          dvtm.loadDvtResources("js");
        }

    }

    /**
     * Initializes the gauge amxNode on refresh by copying the old amxNode properties.
     * Should be called from tag:refresh.
     */
    dvtm.initGaugeAmxNodeForRefresh = function(oldAmxNode, newAmxNode) {
        // make a note that this is a refresh phase
        newAmxNode["_refreshing"] = true;

        // copy the reference to the previous chart object
        if (oldAmxNode._theChartObject != undefined) {
            newAmxNode._theChartObject = oldAmxNode._theChartObject;
        }

        // clear the 'dirty' flag on the options object
        dvtm.setOptionsDirty(newAmxNode, false);
        // optionsObject will be mostly reused
        newAmxNode["_optionsObj"] = oldAmxNode["_optionsObj"];

        // data object will be recreated from scratch
        newAmxNode["_dataObj"] = {};
    }

    /*
     * Initializes the DOM node corresponding to the chart's 'div'.
     * Should be called as the first thing in tag:init.
     */
    dvtm.initChartDomNode = function($node, amxNode) {
        
        // if 'inlineStyle' is defined on the chart, apply it to node
        if (amxNode.inlineStyle) {
            $node.css(amxNode.inlineStyle);
        }

        $node.tap(function() {
            if (amx.acceptEvent()) {
                amx.processAmxEvent($node,"action");
            }
        });
        
    }

    /**
     * sets up chart's outer div element
     */
    dvtm.setupChartDiv = function(amxNode) {

        var idAttr = '';

        if (amxNode.id) {
            idAttr = 'id="' + amxNode.id + '"';
        }
        // this is the default chart div with 100% width/height
        var $content = $('<div ' + idAttr + ' style="width: 100%; height: 100%;"></div>');

        // append markup div's to be able to resolve styles later in tag:init

        if (!dvtm.isGauge(amxNode) && !dvtm.isSparkChart(amxNode)) {

            var $titleDiv = $('<div id="_titleDiv" class="dvtm-chartTitle"></div>');
            $content.append($titleDiv);

            var $subtitleDiv = $('<div id="_subtitleDiv" class="dvtm-chartSubtitle"></div>');
            $content.append($subtitleDiv);

            var $footnoteDiv = $('<div id="_footnoteDiv" class="dvtm-chartFootnote"></div>');
            $content.append($footnoteDiv);

            var $titleSeparatorDiv = $('<div id="_titleSeparatorDiv" class="dvtm-chartTitleSeparator"></div>');
            $content.append($titleSeparatorDiv);
        }

        var $legendDiv = $('<div id="_legendDiv" class="dvtm-chartLegend"></div>');
        $content.append($legendDiv);

        var $legendTitleDiv = $('<div id="_legendTitleDiv" class="dvtm-chartLegendTitle"></div>');
        $content.append($legendTitleDiv);

        if (!dvtm.isGauge(amxNode) && !dvtm.isSparkChart(amxNode)) {

            var $xAxisTitleDiv = $('<div id="_xAxisTitleDiv" class="dvtm-chartXAxisTitle"></div>');
            $content.append($xAxisTitleDiv);

            var $yAxisTitleDiv = $('<div id="_yAxisTitleDiv" class="dvtm-chartYAxisTitle"></div>');
            $content.append($yAxisTitleDiv);

            var $y2AxisTitleDiv = $('<div id="_y2AxisTitleDiv" class="dvtm-chartY2AxisTitle"></div>');
            $content.append($y2AxisTitleDiv);

            var $xAxisTickLabelDiv = $('<div id="_xAxisTickLabelDiv" class="dvtm-chartXAxisTickLabel"></div>');
            $content.append($xAxisTickLabelDiv);

            var $yAxisTickLabelDiv = $('<div id="_yAxisTickLabelDiv" class="dvtm-chartYAxisTickLabel"></div>');
            $content.append($yAxisTickLabelDiv);

            var $y2AxisTickLabelDiv = $('<div id="_y2AxisTickLabelDiv" class="dvtm-chartY2AxisTickLabel"></div>');
            $content.append($y2AxisTickLabelDiv);

            var $pieLabelDiv = $('<div id="_pieLabelDiv" class="dvtm-chartPieLabel"></div>');
            $content.append($pieLabelDiv);

            var $sliceLabelDiv = $('<div id="_sliceLabelDiv" class="dvtm-chartSliceLabel"></div>');
            $content.append($sliceLabelDiv);
        }

        if (dvtm.isGauge(amxNode)) {
            var $gaugeLabelDiv = $('<div id="_gaugeLabelDiv" class="dvtm-gaugeLabel"></div>');
            $content.append($gaugeLabelDiv);
        }

        return $content;
    }            


    /**
     * returns the chart type
     */
    dvtm.getChartType = function(amxNode) {
        var options = amxNode["_optionsObj"];
        return options.chart.type;
    }

    /**
     * sets the chart type property on the options object
     */
    dvtm.setChartType = function(amxNode, type) {
        var options = amxNode["_optionsObj"];

        if (options.chart == undefined) {
            options.chart = {};
        }
        if (options.chart.type !== type) {
            options.chart.type = type;
            dvtm.setOptionsDirty(amxNode, true);
        }
    }
    
    /**
     * sets common chart properties found on the amxNode
     */
    dvtm.setCommonChartProperties = function(amxNode) {

        var options = amxNode["_optionsObj"];
        var data = amxNode["_dataObj"];
        var refreshing = amxNode["_refreshing"];

        // if style template exists, apply its properties first
        // don't bother setting the style defaults on refresh though
        if (!refreshing && dvtm.styleXml) {

            var color;
            var style
            var value;

            // if we got here, assume the options object *will* be modified
            dvtm.setOptionsDirty(amxNode, true);

            var chartTag = $(dvtm.styleXml).find('chart')[0];
            if (chartTag) {

                if ($(chartTag).attr('animationDuration')) {
                    options.styleDefaults.animationDuration = $(chartTag).attr('animationDuration') * 1;
                }
                if ($(chartTag).attr('animationIndicators')) {
                    options.styleDefaults.animationIndicators = $(chartTag).attr('animationIndicators');
                }
                if ($(chartTag).attr('animationDownColor')) {
                    options.styleDefaults.animationDownColor = $(chartTag).attr('animationDownColor');
                }
                if ($(chartTag).attr('animationUpColor')) {
                    options.styleDefaults.animationUpColor = $(chartTag).attr('animationUpColor');
                }
                if ($(chartTag).attr('animationOnDisplay')) {
                    options.chart.animationOnDisplay = $(chartTag).attr('animationOnDisplay');
                }
                if ($(chartTag).attr('animationOnDataChange')) {
                    options.chart.animationOnDataChange = $(chartTag).attr('animationOnDataChange');
                }
                if ($(chartTag).attr('pieFeelerColor')) {
                    color = $(chartTag).attr('pieFeelerColor');
                    options.styleDefaults.pieFeelerColor = color;
                }
            }

            var defaultColorsTag = $(dvtm.styleXml).find('defaultColors')[0];
            if (defaultColorsTag) {
                var colors = $(defaultColorsTag).children('color');
                options.styleDefaults.colors = [];
                $.each(colors, function(index, item) {
                    dvtm.defaultColors[$(item).attr('index')] = $(item).attr('value');
                    options.styleDefaults.colors[$(item).attr('index')] = $(item).attr('value');
                });
            }
            var chartDataItemTag = $(dvtm.styleXml).find('chartDataItem')[0];
            if (chartDataItemTag) {
                if ($(chartDataItemTag).attr('borderColor')) {
                    color = $(chartDataItemTag).attr('borderColor');
                    options.styleDefaults.borderColor = color;
                }
                if ($(chartDataItemTag).attr('markerColor')) {
                    color = $(chartDataItemTag).attr('markerColor');
                    options.styleDefaults.markerColor = color;
                }
                if ($(chartDataItemTag).attr('markerDisplayed')) {
                    value = $(chartDataItemTag).attr('markerDisplayed');
                    options.styleDefaults.markerDisplayed = (value === true || value === 'true') ? true : false;
                }
                if ($(chartDataItemTag).attr('markerShape')) {
                    var shape = $(chartDataItemTag).attr('markerShape');
                    options.styleDefaults.markerShape = shape;
                }
                if ($(chartDataItemTag).attr('markerSize')) {
                    value = $(chartDataItemTag).attr('markerSize') * 1;
                    options.styleDefaults.markerSize = value;
                }
            }

            var seriesStyleTag = $(dvtm.styleXml).find('seriesStyle')[0];
            if (seriesStyleTag) {
                if ($(seriesStyleTag).attr('lineStyle')) {
                    style = $(seriesStyleTag).attr('lineStyle');
                    options.styleDefaults.lineStyle = style;
                }
                if ($(seriesStyleTag).attr('lineWidth')) {
                    value = $(seriesStyleTag).attr('lineWidth') * 1;
                    options.styleDefaults.lineWidth = value;
                }
                if ($(seriesStyleTag).attr('seriesEffect')) {
                    value = $(seriesStyleTag).attr('seriesEffect');
                    options.styleDefaults.seriesEffect = value;
                }
            }
        }

        if (amxNode.title)
            data.title = amxNode.title;
       
	if (amxNode.titleHAlign !== options.title.hAlign) {
	    options.title.hAlign = amxNode.titleHAlign;
            dvtm.setOptionsDirty(amxNode, true);
        }

	if (amxNode.subtitle)
	    data.subtitle = amxNode.subtitle;

	if (amxNode.subtitleHAlign !== options.subtitle.hAlign) {
	    options.subtitle.hAlign = amxNode.subtitleHAlign;
            dvtm.setOptionsDirty(amxNode, true);
        }

	if (amxNode.footnote)
	    data.footnote = amxNode.footnote;
	
	if (amxNode.footnoteHAlign !== options.footnote.hAlign) {
	    options.footnote.hAlign = amxNode.footnoteHAlign;
            dvtm.setOptionsDirty(amxNode, true);
        }
        
        if (amxNode.timeAxisType)
            data.timeAxisType = amxNode.timeAxisType;
 
        if (amxNode.animationOnDisplay && (amxNode.animationOnDisplay !== options.chart.animationOnDisplay)) {
            options.chart.animationOnDisplay = amxNode.animationOnDisplay;
            dvtm.setOptionsDirty(amxNode, true);
        }

	if (amxNode.animationOnDataChange && (amxNode.animationOnDataChange !== options.chart.animationOnDataChange)) {
	    options.chart.animationOnDataChange = amxNode.animationOnDataChange;
            dvtm.setOptionsDirty(amxNode, true);
        }
        
        if (amxNode.animationDuration && (amxNode.animationDuration !== options.styleDefaults.animationDuration)) {
            options.styleDefaults.animationDuration = +amxNode.animationDuration;
            dvtm.setOptionsDirty(amxNode, true);
        }

        if (amxNode.animationIndicators && (amxNode.animationIndicators !== options.styleDefaults.animationIndicators)) {
            options.styleDefaults.animationIndicators = amxNode.animationIndicators;
            dvtm.setOptionsDirty(amxNode, true);
        }

        if (amxNode.animationDownColor && (amxNode.animationDownColor !== options.styleDefaults.animationDownColor)) {
            options.styleDefaults.animationDownColor = amxNode.animationDownColor;
            dvtm.setOptionsDirty(amxNode, true);
        }

        if (amxNode.animationUpColor && (amxNode.animationUpColor !== options.styleDefaults.animationUpColor)) {
            options.styleDefaults.animationUpColor = amxNode.animationUpColor;
            dvtm.setOptionsDirty(amxNode, true);
        }

        if (amx.dtmode) {
            // bug 13810977: in DT mode, disable interactive behavior
            options.chart.dataSelection = 'none';
            options.chart.hideAndShowBehavior = 'none';
            options.chart.rolloverBehavior = 'none';
        }
        else {
            if (amxNode.dataSelection !== options.chart.dataSelection) {
                options.chart.dataSelection = amxNode.dataSelection;
                dvtm.setOptionsDirty(amxNode, true);
            }

            if (amxNode.hideAndShowBehavior !== options.chart.hideAndShowBehavior) {
                options.chart.hideAndShowBehavior = amxNode.hideAndShowBehavior;
                dvtm.setOptionsDirty(amxNode, true);
            }

            if (amxNode.rolloverBehavior !== options.chart.rolloverBehavior) {
                options.chart.rolloverBehavior = amxNode.rolloverBehavior;
                dvtm.setOptionsDirty(amxNode, true);
            }
        }

        if (amxNode.stack !== options.chart.stack) {
            options.chart.stack = amxNode.stack;
            dvtm.setOptionsDirty(amxNode, true);
        }

	if (amxNode.emptyText !== options.chart.emptyText) {
	    options.chart.emptyText = amxNode.emptyText;
            dvtm.setOptionsDirty(amxNode, true);
        }

        if (amxNode.tagName == "dvtm:sparkChart") {
            if (amxNode.type !== options.type) {
                options.type = amxNode.type;
                dvtm.setOptionsDirty(amxNode, true);
            }
        }
    }
    
    /**
     * sets common gauge properties found on the amxNode
     */
    dvtm.setCommonGaugeProperties = function(amxNode) {
        var options = amxNode["_optionsObj"];
        var data = amxNode["_dataObj"];
        var refreshing = amxNode["_refreshing"];

        // if style template exists, apply its properties first
        if (!refreshing && dvtm.styleXml) {

            // if we get here, the the options object will be modified for sure
            dvtm.setOptionsDirty(amxNode, true);

            var gaugeTag = $(dvtm.styleXml).find('gauge')[0];
            if (gaugeTag) {
                if ($(gaugeTag).attr('animationDuration')) {
                    options.animationDuration = $(gaugeTag).attr('animationDuration');
                }
                if ($(gaugeTag).attr('animationOnDisplay')) {
                    options.animationOnDisplay = $(gaugeTag).attr('animationOnDisplay');
                }
                if ($(gaugeTag).attr('animationOnDataChange')) {
                    options.animationOnDataChange = $(gaugeTag).attr('animationOnDataChange');
                }
            }
        }

        // now set the attribute values (may overridevalues set in the skin)
        if (amxNode.animationDuration && (amxNode.animationDuration !== options.animationDuration)) {
            options.animationDuration = +amxNode.animationDuration;
            dvtm.setOptionsDirty(amxNode, true);
        }
        if (amxNode.animationOnDisplay && (amxNode.animationOnDisplay !== options.animationOnDisplay)) {
            options.animationOnDisplay = amxNode.animationOnDisplay;
            dvtm.setOptionsDirty(amxNode, true);
        }
        if (amxNode.animationOnDataChange && (amxNode.animationOnDataChange !== options.animationOnDataChange)) {
            options.animationOnDataChange = amxNode.animationOnDataChange;
            dvtm.setOptionsDirty(amxNode, true);
        }
        if (amxNode.borderColor !== options.borderColor) {
            options.borderColor = amxNode.borderColor;
            dvtm.setOptionsDirty(amxNode, true);
        }
        if (amxNode.color !== options.color) {
            options.color = amxNode.color;
            dvtm.setOptionsDirty(amxNode, true);
        }
        if (amxNode.labelDisplay !== options.labelDisplay) {
            options.labelDisplay = amxNode.labelDisplay;
            dvtm.setOptionsDirty(amxNode, true);
        }
        if (amxNode.emptyText !== options.emptyText) {
            options.emptyText = amxNode.emptyText;
            dvtm.setOptionsDirty(amxNode, true);
        }
        if (amxNode.type !== options.type) {
            options.type = amxNode.type;
            dvtm.setOptionsDirty(amxNode, true);
        }
        if (amxNode.visualEffects !== options.visualEffects) {
            options.visualEffects = amxNode.visualEffects;
            dvtm.setOptionsDirty(amxNode, true);
        }
        if (amxNode.value) {
            data.value = +amxNode.value;
        }
        if (amxNode.minValue) {
            data.minValue = +amxNode.minValue;
        }
        if (amxNode.maxValue) {
            data.maxValue = +amxNode.maxValue;
        }
        if (amxNode.shortDesc) {
            data.tooltip = amxNode.shortDesc;
        }
    }

    /**
     * adds a name/data pair to the series.  The item must be of type
     * { name: X, data: Y }.
     */
    dvtm.addSeriesItem = function(amxNode, item) {
        
        amxNode["_dataObj"].series.push(item);
    }
    
    
    /**
     * Sets properties of a legend.
     *
     * The following properties are supported:
     *   backgroundColor - style template
     *   borderColor     - style template
     *   position        - tag attribute
     *   scrolling       - tag attribute
     *   textStyle       - style template
     *   titleHAlign     - tag attribute
     *   titleStyle      - style template
     *   title           - tag attribute
     */
    dvtm.setLegendProperties = function(amxNode, legend) {
        
        var options = amxNode["_optionsObj"];
        var data = amxNode["_dataObj"];

        // legend position - auto, start, end, bottom, top, none
        if (legend.position !== options.legend.position) {
            options.legend.position = legend.position;
            dvtm.setOptionsDirty(amxNode, true);
        }
        
        // legend scrolling - off, on, asNeeded
        if (legend.scrolling !== options.legend.scrolling) {
            options.legend.scrolling = legend.scrolling;
            dvtm.setOptionsDirty(amxNode, true);
        }

        // legend title alignment - start, end, center
        if (legend.titleHAlign !== options.legend.titleHalign) {
            options.legend.titleHalign = legend.titleHAlign;
            dvtm.setOptionsDirty(amxNode, true);
        }

        // legend title
        if (legend.title) {
            data.legend.title = legend.title;
        }
        // legend referenceObjectTitle
        if (legend.referenceObjectTitle) {
            data.legend.referenceObjectTitle = legend.referenceObjectTitle;
        }
    }
    
    
    /**
     * returns the chart width
     */
    dvtm.getChartWidth = function($node, amxNode) {
        
        var width = $node.width();

        // If the element width is non-zero, assume it has been set via css or derived
        // from document. Otherwise, return the default width for the chart type.
        if (width <= 1) {
            if (dvtm.isSparkChart(amxNode)) {
                width = dvtm.DEFAULT_SPARK_CHART_WIDTH;
            }
            else if (dvtm.isGauge(amxNode)) {
                if (amxNode.tagName === 'dvtm:ledGauge') {
                    width = dvtm.DEFAULT_LED_GAUGE_WIDTH;
                }
                else {
                    width = dvtm.DEFAULT_STATUS_METER_GAUGE_WIDTH;
                }
            }
            else {
                width = dvtm.DEFAULT_CHART_WIDTH;
            }
            $node.width(width);
        }

        return width;
    }

    /**
     * returns the chart height
     */
    dvtm.getChartHeight = function($node, amxNode) {
        
        var height = $node.height();

        // If the element height is non-zero, assume it has been set via css or derived
        // from document. Otherwise, return the default height for the chart type.
        if (height <= 1) {
            if (dvtm.isSparkChart(amxNode)) {
                height = dvtm.DEFAULT_SPARK_CHART_HEIGHT;
            }
            else if (dvtm.isGauge(amxNode)) {
                if (amxNode.tagName === 'dvtm:ledGauge') {
                    height = dvtm.DEFAULT_LED_GAUGE_HEIGHT;
                }
                else {
                    height = dvtm.DEFAULT_STATUS_METER_GAUGE_HEIGHT;
                }
            }
            else {
                height = dvtm.DEFAULT_CHART_HEIGHT;
            }
            $node.height(height);
        }
        return height;
    }
    
    /**
     * build css style string
     */
     dvtm.buildCssStyleString = function(div) {

        var style = {};
        var styleString = "";

        if ($(div).css('font-family')) {
          style.fontFamily = $(div).css('font-family');
          styleString += "font-family: " + style.fontFamily + ";";
        }
        if ($(div).css('font-size')) {
          style.fontSize = $(div).css('font-size');
          styleString += "font-size: " + style.fontSize + ";";
        }
        if ($(div).css('font-weight')) {
          style.fontWeight = $(div).css('font-weight');
          styleString += "font-weight: " + style.fontWeight + ";";
        }
        if ($(div).css('color')) {
          style.color = $(div).css('color');
          styleString += "color: " + style.color + ";";
        }
        
        // TODO more css properties to be added
        
        return styleString;
     }

    /**
     * sets style properties based on CSS
     */
    dvtm.setCssBasedProperties = function($node, amxNode) {
        
        var options = amxNode["_optionsObj"];

        // if we get here, assume the options objec *will* be modified
        dvtm.setOptionsDirty(amxNode, true);

        // chart background color
        var bgColor = $node.css('background-color');
        if (bgColor) 
            options.plotArea = {backgroundColor: bgColor};

        if (dvtm.isGauge(amxNode)) {
            // gauge border color, unless already specified via attribute
            if (options.borderColor === undefined) {
                var borderColor = $node.css('border-bottom-color');
                if (borderColor)
                    options.borderColor = borderColor;
            }
            // gauge color, unless already specified via attribute
            if (options.color === undefined) {
                var gaugeColor = $node.css('color');
                if (gaugeColor)
                    options.color = gaugeColor;
            }
        }

        if (!dvtm.isGauge(amxNode) && !dvtm.isSparkChart(amxNode)) {

            // title style
            var titleDiv = $node.children("#_titleDiv")[0];
            var titleStyleString = this.buildCssStyleString(titleDiv);
            options.title.style = titleStyleString;
            // cleanup the div, we don't need it anymore
            $(titleDiv).remove();

            // subtitle style
            var subtitleDiv = $node.children("#_subtitleDiv")[0];
            var subtitleStyleString = this.buildCssStyleString(subtitleDiv);
            options.subtitle.style = subtitleStyleString;
            // cleanup the div, we don't need it anymore
            $(subtitleDiv).remove();

            // footnote style
            var footnoteDiv = $node.children("#_footnoteDiv")[0];
            var footnoteStyleString = this.buildCssStyleString(footnoteDiv);
            options.footnote.style = footnoteStyleString;
            // cleanup the div, we don't need it anymore
            $(footnoteDiv).remove();

            // title separator styles
            var titleSeparatorDiv = $node.children("#_titleSeparatorDiv")[0];
            if ($(titleSeparatorDiv).css('visibility'))
                options.titleSeparator.rendered = ($(titleSeparatorDiv).css('visibility') == 'visible');
            if ($(titleSeparatorDiv).css('border-top-color'))
                options.titleSeparator.upperColor = $(titleSeparatorDiv).css('border-top-color');
            if ($(titleSeparatorDiv).css('border-bottom-color'))
                options.titleSeparator.lowerColor = $(titleSeparatorDiv).css('border-bottom-color');
            // cleanup the div, we don't need it anymore
            $(titleSeparatorDiv).remove();
        }

        // legend styles
        var legendDiv = $node.children("#_legendDiv")[0];
        if ($(legendDiv).css('background-color'))
            options.legend.backgroundColor = $(legendDiv).css('background-color');
        if ($(legendDiv).css('border-top-color'))       // need to use specific border part, e.g. 'top'
            options.legend.borderColor = $(legendDiv).css('border-top-color');
        var legendStyleString = this.buildCssStyleString(legendDiv);
        options.legend.textStyle = legendStyleString;
        // cleanup the div, we don't need it anymore
        $(legendDiv).remove();

        // legend title style
        var legendTitleDiv = $node.children("#_legendTitleDiv")[0];
        var legendTitleStyleString = this.buildCssStyleString(legendTitleDiv);
        options.legend.titleStyle = legendTitleStyleString;
        // cleanup the div, we don't need it anymore
        $(legendTitleDiv).remove();

        if (!dvtm.isGauge(amxNode) && !dvtm.isSparkChart(amxNode)) {
            // xAxis styles
            var xAxisTitleDiv = $node.children("#_xAxisTitleDiv")[0];
            var xAxisTitleStyleString = this.buildCssStyleString(xAxisTitleDiv);
            options.xAxis.titleStyle = xAxisTitleStyleString;
            // cleanup the div, we don't need it anymore
            $(xAxisTitleDiv).remove();

            var xAxisTickLabelDiv = $node.children("#_xAxisTickLabelDiv")[0];
            var xAxisTickLabelStyleString = this.buildCssStyleString(xAxisTickLabelDiv);
            options.xAxis.tickLabel.style = xAxisTickLabelStyleString;
            // cleanup the div, we don't need it anymore
            $(xAxisTickLabelDiv).remove();

            // yAxis styles
            var yAxisTitleDiv = $node.children("#_yAxisTitleDiv")[0];
            var yAxisTitleStyleString = this.buildCssStyleString(yAxisTitleDiv);
            options.yAxis.titleStyle = yAxisTitleStyleString;
            // cleanup the div, we don't need it anymore
            $(yAxisTitleDiv).remove();

            var yAxisTickLabelDiv = $node.children("#_yAxisTickLabelDiv")[0];
            var yAxisTickLabelStyleString = this.buildCssStyleString(yAxisTickLabelDiv);
            options.yAxis.tickLabel.style = yAxisTickLabelStyleString;
            // cleanup the div, we don't need it anymore
            $(yAxisTickLabelDiv).remove();

            // y2Axis styles
            var y2AxisTitleDiv = $node.children("#_y2AxisTitleDiv")[0];
            var y2AxisTitleStyleString = this.buildCssStyleString(y2AxisTitleDiv);
            options.y2Axis.titleStyle = y2AxisTitleStyleString;
            // cleanup the div, we don't need it anymore
            $(y2AxisTitleDiv).remove();

            var y2AxisTickLabelDiv = $node.children("#_y2AxisTickLabelDiv")[0];
            var y2AxisTickLabelStyleString = this.buildCssStyleString(y2AxisTickLabelDiv);
            options.y2Axis.tickLabel.style = y2AxisTickLabelStyleString;
            // cleanup the div, we don't need it anymore
            $(y2AxisTickLabelDiv).remove();

            // pieLabel style
            var pieLabelDiv = $node.children("#_pieLabelDiv")[0];
            var pieLabelStyleString = this.buildCssStyleString(pieLabelDiv);
            options.styleDefaults.pieLabelStyle = pieLabelStyleString;
            // cleanup the div, we don't need it anymore
            $(pieLabelDiv).remove();

            // sliceLabel style
            var sliceLabelDiv = $node.children("#_sliceLabelDiv")[0];
            var sliceLabelStyleString = this.buildCssStyleString(sliceLabelDiv);
            options.styleDefaults.sliceLabel.style = sliceLabelStyleString;
            // cleanup the div, we don't need it anymore
            $(sliceLabelDiv).remove();
        }

        if (dvtm.isGauge(amxNode)) {
            // gaugeLabel style
            var gaugeLabelDiv = $node.children("#_gaugeLabelDiv")[0];
            var gaugeLabelStyleString = this.buildCssStyleString(gaugeLabelDiv);
            options.labelStyle = gaugeLabelStyleString;
            // cleanup the div, we don't need it anymore
            $(gaugeLabelDiv).remove();
        }
    }
    
    /**
     * renders the chart SVG and appends it to the parent node
     */
    dvtm.renderChart = function($node, amxNode) {
        
        var options = amxNode["_optionsObj"];
        var data = amxNode["_dataObj"];
        var refreshing = amxNode["_refreshing"];

        // first, get the width and height from style
        var width = dvtm.getChartWidth($node, amxNode);
        var height = dvtm.getChartHeight($node, amxNode);

        var id;
        var context;

        // on refresh, if the options object is empty, delete the old nodes content
        // and recreate the chart
        if (refreshing && dvtm.isOptionsDirty(amxNode)) {
            $node.empty();
        }

        // if not refreshing, or if the options object is dirty, or we're missing
        // the previous chart object, create the document and context
        if (!refreshing || dvtm.isOptionsDirty(amxNode) || (amxNode._theChartObject == undefined)) {

            // on Android devices use Canvas rendering instead of SVG
            // TODO: Android > 3.x supports SVG, need support in the agent (bug: 13397133)
            var agentId = DvtAgent.getAgent().getName();
            var bUseCanvas = agentId.indexOf('android') >= 0;
            //console.log("AgentId: " + agentId);

            if (!bUseCanvas) {
                id = (amxNode.id) ? amxNode.id + '_svg' : 'chart_svg';
                // Create the SVG Element
                var svg = DvtSvgUtils.createSvgDocument(id);
                // append the svg node to the parent
                $node.append(svg);
                // Create the component and pass in the rendering context
                context = new DvtSvgContext(svg);
            }
            else {
                id = (amxNode.id) ? amxNode.id + '_canvas' : 'chart_canvas';
                // create the Canvas element
                var canvas = DvtCanvasUtils.createCanvasDocument(width, height, id);
                // append the canvas node to the parent
                $node.append(canvas);
                // create the component and pass in the rendering context
                context = new DvtCanvasContext(canvas);
            }

            var selectionCallbackObj = null;

            if (amxNode.selectionListener != undefined) {

                selectionCallbackObj = {

                    selectionListener: amxNode.selectionListener,

                    callback: function(event, component) {
                        // fire the selectionChange event
                        if (event._selection) {
                            amx.processAmxEvent($node, 'selectionChange');
                        }
                        // TODO: fire a proper selection event with parameters
                        //       to notify the registered selectionListener
                    }
                }
            }

            // TODO: debug print - remove!
            //dvtm.dumpOptionsObject(amxNode);

            if (amxNode.tagName == "dvtm:sparkChart") {
                amxNode._theChartObject = DvtSparkChart.newInstance(context, null, null, options);
            } else if (amxNode.tagName == "dvtm:ledGauge"){
                amxNode._theChartObject = DvtLedGauge.newInstance(context, null, null, options);
            } else if (amxNode.tagName == "dvtm:statusMeterGauge") {
                amxNode._theChartObject = DvtStatusMeterGauge.newInstance(context, null, null, options);
            } else {
                if (selectionCallbackObj) {
                    amxNode._theChartObject = DvtChart.newInstance(context, selectionCallbackObj.callback, selectionCallbackObj, options);
                }
                else {
                    amxNode._theChartObject = DvtChart.newInstance(context, null, null, options);
                }
            }
            context.getStage().addChild(amxNode._theChartObject);
        }

        // TODO: debug print - remove!
        //dvtm.dumpDataObject(amxNode);

        // and finally render the chart
        amxNode._theChartObject.render(data, width, height);
    }

    /**
     * sets the graph axis properties
     */
    dvtm.setAxisProperties = function(amxNode, axisNode, axisId) {

        var axis;
        var axisData;

        switch (axisId) {
            case 'xAxis':
                axis = amxNode["_optionsObj"].xAxis;
                axisData = amxNode["_dataObj"].xAxis = {};
                break;
            case 'yAxis':
                axis = amxNode["_optionsObj"].yAxis;
                axisData = amxNode["_dataObj"].yAxis = {};
                break;
            case 'y2Axis':
                axis = amxNode["_optionsObj"].y2Axis;
                axisData = amxNode["_dataObj"].y2Axis = {};
                break;
            default:
                // unsupported axis type!
                return;
        }

        if (axisNode.title) {
            axisData.title = axisNode.title;
        }
        if (axisNode.axisMinValue !== axis.minValue) {
            axis.minValue = +axisNode.axisMinValue;
            dvtm.setOptionsDirty(amxNode, true);
        }
        if (axisNode.axisMaxValue !== axis.maxValue) {
            axis.maxValue = +axisNode.axisMaxValue;
            dvtm.setOptionsDirty(amxNode, true);
        }
        if (axisNode.majorIncrement !== axis.majorIncrement) {
            axis.majorIncrement = +axisNode.majorIncrement;
            dvtm.setOptionsDirty(amxNode, true);
        }
        if (axisNode.minorIncrement !== axis.minorIncrement) {
            axis.minorIncrement = +axisNode.minorIncrement;
            dvtm.setOptionsDirty(amxNode, true);
        }
        if (axisId == 'xAxis' && (axisNode.timeRangeMode !== axis.timeRangeMode)) {
            axis.timeRangeMode = axisNode.timeRangeMode;
            dvtm.setOptionsDirty(amxNode, true);
        }
    }
    
    /**
     * parses the referenceObject node attributes 
     * 
     * referenceObject has the following attributes
     * 
     *   text       - String: tooltip and legend text for this reference object
     *   type       - String: line, area
     *   location   - String: front, back
     *   color      - String(Color): support CSS color values
     *   lineWidth  - Number
     *   lineStyle  - String
     *   lineValue  - Number
     *   lowValue   - Number
     *   highValue  - Number
     *   shortDesc  - String: custom tooltip for this reference object
     *   displayInLegend  - String: on/off - legend item should be added for this ref obj
     *   
     */
    dvtm.processReferenceObj = function(amxNode, referenceObjNode, ref) {
        var referenceObjects = [];
	var refObj = {};
        var dataObj;
	
	switch (ref) {
            case 'xAxis':
                dataObj = amxNode["_dataObj"].xAxis ;
                break;
            case 'yAxis':
                dataObj = amxNode["_dataObj"].yAxis;
                break;
            case 'y2Axis':
                dataObj = amxNode["_dataObj"].y2Axis;
                break;
            case 'spark':
                dataObj = amxNode["_dataObj"];
                break;
            default:
                // unsupported axis type!
                return;
        }
        if (dataObj) {
            if (dataObj.referenceObjects == undefined) {
                dataObj.referenceObjects = [];
            }
            referenceObjects = dataObj.referenceObjects;
        }
        if (referenceObjNode.text) {
            refObj.text = referenceObjNode.text;
        }
        if (referenceObjNode.type) {
            refObj.type = referenceObjNode.type;
        }
        if (referenceObjNode.location) {
            refObj.location = referenceObjNode.location;
        }
        if (referenceObjNode.color) {
            refObj.color = referenceObjNode.color;
        }
        if (referenceObjNode.lineWidth) {
            refObj.lineWidth = +referenceObjNode.lineWidth;
        }
        if (referenceObjNode.lineStyle) {
            refObj.lineStyle = referenceObjNode.lineStyle;
        }
        if (referenceObjNode.lineValue) {
            refObj.lineValue = +referenceObjNode.lineValue;
        }
        if (referenceObjNode.lowValue) {
            refObj.lowValue = +referenceObjNode.lowValue;
        }
        if (referenceObjNode.highValue) {
            refObj.highValue = +referenceObjNode.highValue;
        }
        if (referenceObjNode.shortDesc) {
            refObj.tooltip = referenceObjNode.shortDesc;
        }
        if (referenceObjNode.displayInLegend) {
            refObj.displayInLegend = referenceObjNode.displayInLegend;
        }


	referenceObjects.push(refObj);
    }

    /** parses sliceLabel node attributes
     *  sliceLabel has the following attributes:
     *  
     *  position        - String: none, inside, outside
     *  style           - String: accepts font related CSS attributes
     *  textType        - String: text, value, percent, textAndPercent
     *  scaling         - String: auto, none, thousand, million, billion, trillion, quadrillion
     *  autoPrecision   - String: on (default), off
     *  converter       - Object: numberConverter
     *  
     */
     dvtm.processSliceLabel = function(amxNode, sliceLabelNode) {
         var sliceLabel = amxNode["_optionsObj"].styleDefaults.sliceLabel;
         
         if (sliceLabelNode.position)
         sliceLabel.position = sliceLabelNode.position;
         
         if (sliceLabelNode.textType)
         sliceLabel.textType = sliceLabelNode.textType;
         
         if (sliceLabelNode.scaling)
         sliceLabel.scaling = sliceLabelNode.scaling;
         
         if (sliceLabelNode.autoPrecision)
         sliceLabel.autoPrecision = sliceLabelNode.autoPrecision;
         
         if (sliceLabelNode.converter)
         sliceLabel.converter = sliceLabelNode.converter;
     }
     
     /** parses pieValueFormat node attributes
      *  pieValueFormat has the following attributes:
      *  
      *  converter      - Object: numberConverter
      *
      */
      dvtm.processPieValueFormat = function(amxNode, pieValueFormatNode) {
          var pieValueFormat = amxNode["_optionsObj"].styleDefaults.pieValueFormat;
          
          if (pieValueFormatNode.converter)
          pieValueFormat.converter = pieValueFormatNode.converter;
      }
    
    /** parses tickLabel node attributes
     *  
     *  tickLabel has the following attributes:
     *  
     *  autoPrecision     - String: on, off
     *  rendered          - Boolean: true if the tickLabel should be rendered
     *  scaling           - String: auto, none, thousand, million, billion, trillion, quadrillion
     *  style             - String: font related CSS attributes
     *  converter         - Object: numberConverter or dateTimeConverter
     *  
     */
     dvtm.processTickLabel = function(amxNode, tickLabelNode, axisId) {

	var axis;
        switch (axisId) {
            case 'xAxis':
                axis = amxNode["_optionsObj"].xAxis;
                break;
            case 'yAxis':
                axis = amxNode["_optionsObj"].yAxis;
                break;
            case 'y2Axis':
                axis = amxNode["_optionsObj"].y2Axis;
                break;
            default:
                // unsupported axis type!
                return;
        }
	if (axis) {
            if (axis.tickLabel == undefined) {
                axis.tickLabel = {};
            }
		
        }

	if (tickLabelNode.autoPrecision !== axis.tickLabel.autoPrecision) {
	    axis.tickLabel.autoPrecision = tickLabelNode.autoPrecision;
            dvtm.setOptionsDirty(amxNode, true);
	}
	if (tickLabelNode.rendered !== axis.tickLabel.rendered) {
	    axis.tickLabel.rendered = (tickLabelNode.rendered === 'true' || tickLabelNode.rendered === true) ? true : false;
            dvtm.setOptionsDirty(amxNode, true);
	}
	if (tickLabelNode.scaling !== axis.tickLabel.scaling) {
	    axis.tickLabel.scaling = tickLabelNode.scaling;
            dvtm.setOptionsDirty(amxNode, true);
	}
        // if amx:convertNumber or amx:convertDateTime is used as a child tag of the tickLabel,
        // then the tickLabelNode would have a converter object
        // we pass that converter to js chart API
        if (tickLabelNode.converter) {
            axis.tickLabel.converter = tickLabelNode.converter;
        }
        

    }
    
    /**
     * parses axisLine node attributes
     * 
     * axisLine has the following attributes:
     * 
     * lineColor      - String(Color): support CSS color values
     * lineWidth      - Number: e.g. 1
     * rendered       - Boolean: true if the axisLine should be rendered
     * 
     */
     dvtm.processAxisLine = function(amxNode, axisLineNode, axisId) {
	
	var axis;
        switch (axisId) {
            case 'xAxis':
                axis = amxNode["_optionsObj"].xAxis;
                break;
            case 'yAxis':
                axis = amxNode["_optionsObj"].yAxis;
                break;
            case 'y2Axis':
                axis = amxNode["_optionsObj"].y2Axis;
                break;
            default:
                // unsupported axis type!
                return;
	}
	if (axis) {
            if (axis.axisLine == undefined) {
                axis.axisLine = {};
            }
		
        }

	if (axisLineNode.lineColor !== axis.axisLine.lineColor) {
	    axis.axisLine.lineColor = axisLineNode.lineColor;
            dvtm.setOptionsDirty(amxNode, true);
	}
	if (axisLineNode.lineWidth !== axis.axisLine.lineWidth) {
	    axis.axisLine.lineWidth = +axisLineNode.lineWidth;
            dvtm.setOptionsDirty(amxNode, true);
	}
	if (axisLineNode.rendered !== axis.axisLine.rendered) {
	    axis.axisLine.rendered = (axisLineNode.rendered === 'true' || axisLineNode.rendered === true) ? true : false;
            dvtm.setOptionsDirty(amxNode, true);
	}
    }

    /**
     * processes major/minorTick node attributes
     *
     * tick has the following attributes:
     *
     * lineColor      - String(Color): support CSS color values
     * lineWidth      - Number: e.g. 1
     * rendered       - Boolean: true if the tick should be rendered
     *                  default true for major, false for minor ticks
     */
     dvtm.processTick = function(amxNode, tickNode, axisId, isMajorTick) {

	var axis;
        var tick;

        switch (axisId) {
            case 'xAxis':
                axis = amxNode["_optionsObj"].xAxis;
                break;
            case 'yAxis':
                axis = amxNode["_optionsObj"].yAxis;
                break;
            case 'y2Axis':
                axis = amxNode["_optionsObj"].y2Axis;
                break;
            default:
                // unsupported axis type!
                return;
	}
	if (axis) {
            if (isMajorTick) {
                if (axis.majorTick === undefined) {
                    axis.majorTick = {};
                }
                tick = axis.majorTick;
            }
            else {
                if (axis.minorTick === undefined) {
                    axis.minorTick = {};
                }
                tick = axis.minorTick;
            }
        }

	if (tickNode.lineColor !== tick.lineColor) {
	    tick.lineColor = tickNode.lineColor;
            dvtm.setOptionsDirty(amxNode, true);
	}
	if (tickNode.lineWidth !== tick.lineWidth) {
	    tick.lineWidth = +tickNode.lineWidth;
            dvtm.setOptionsDirty(amxNode, true);
	}
	if (tickNode.rendered !== tick.rendered) {
            tick.rendered = amx.isValueTrue(tickNode.rendered) ? true : false;
            dvtm.setOptionsDirty(amxNode, true);
	}

     }

    /**
     * parses marker attributes (and potential attribute groups and
     * stores the internal marker representation in the following format
     * 
     * marker : {
     *     seriesId,      // optional
     *     groupId,       // optional
     *     series,        // optional
     *     group,         // optional
     *     label,         // optional
     *     x,             // required
     *     y,             // required
     *     z,             // required for bubble chart
     *     markerSize,    // optional, marker size (does not apply to bubble)
     *     borderColor,   // optional
     *     color,         // optional, may come from attribute groups
     *     markerShape,         // optional, may come from attribute groups
     *     shortDesc,     // optional tooltip
     *     value,         // data item value for area/bar/line charts
     *     markerDisplayed  // optional, true by default
     *     attrGroups     // optional, array of attribute group references
     *     
     *    
     */
    dvtm.processMarker = function(amxNode, markerNode, dataStamp) {

        // first check if this data item should be rendered at all
        if (markerNode.rendered != undefined) {
            if (amx.isValueFalse(markerNode.rendered))
                return null;
        }

        var marker = {attrGroups : []};
        
        if (markerNode.groupId != undefined) {
            marker.groupId = markerNode.groupId;
        }

        if (markerNode.group != undefined) {
            marker.group = markerNode.group;
        }
        
        if (markerNode.seriesId != undefined) {
            marker.seriesId = markerNode.seriesId;
        }
        else if (markerNode.series != undefined) {
            marker.seriesId = markerNode.series;
        }

        if (markerNode.series != undefined) {
            marker.series = markerNode.series;
        }
        else {
            // need at least one anonymous series, if no series name/id is defined
            marker.series = "";
            if (marker.seriesId == undefined) {
                marker.seriesId = "_1";
            }
        }
        
        if (markerNode.x) {
            marker.x = +markerNode.x;
        }
        if (markerNode.y) {
            marker.y = +markerNode.y;
        }
        if (markerNode.z) {
            marker.z = +markerNode.z;
        }
        if (markerNode.markerSize) {
            marker.markerSize = +markerNode.markerSize;
        }
        if (markerNode.value) {
            marker.value = +markerNode.value;
        }
        if (markerNode.borderColor) {
            marker.borderColor = markerNode.borderColor;
        }
        if (markerNode.color) {
            marker.color = markerNode.color;
        }
        if (markerNode.markerShape) {
            marker.shape = markerNode.markerShape;
        }
        if (markerNode.shortDesc) {
            marker.shortDesc = markerNode.shortDesc;
        }
        if (markerNode.visible) {
            marker.visible = (markerNode.visible === 'true') ? true : markerNode.visible ;
        }
        
        // add the marker to the internal list
        amxNode["_markers"].push(marker);

        return marker;
    }


    /**
     * parses the attributeGroups node attributes and
     * updates the internal attributeGroups map
     * 
     * attributeGroups has the following attributes
     * 
     *   id    - optional (?)
     *   type  - list of marker attributes affected by the attributeGroup
     *           (attribute names separated by spaces)
     *   value - a key to group items by
     *   
     * returns a reference to the newly created or existing attribute group
     * 
     */
    dvtm.processAttributeGroup = function(amxNode, marker, attrGroupsNode, index) {

        var attrGroups = amxNode["_attributeGroups"];

        // assuming attrGroup can have id, type and value attributes
        var id;
        
        if (attrGroupsNode.id) {
            id = attrGroupsNode.id;
        }
        else {
            // if no id exists, we'll create one internally
            id = "__" + index;
        }

        // find the attr group by id, 
        // if the attr group with this id doesn't exist yet, create it
        var g = 0;
        for (g = 0; g < attrGroups.length; g++) {
            if (attrGroups[g].id === id)
                break;
        }

        var attrGrp;
        if (g >= attrGroups.length) {
            attrGrp = {id: id,
                        type: attrGroupsNode.type,
                        items: []
                      };
            attrGroups.push(attrGrp);
        }
        else
            attrGrp = attrGroups[g];

        // add new value, if unique
        var items = attrGrp.items;
        var k = 0;
        for (k = 0; k < items.length; k++) {
            if (items[k].value === attrGroupsNode.value)
                break;   // value exists, exit here
        }
        // new value, add it
        if (k >= items.length) {
            var item = {value: attrGroupsNode.value};
            // if this attributeGroup has a label, add it
            if (attrGroupsNode.label != undefined)
                item.label = attrGroupsNode.label;

            attrGrp.items.push(item);
        }
        // add this attrGroupInfo to the current marker
        marker.attrGroups.push( {attrGroup: attrGrp, valueIndex: k} );
        
    }

    /**
     * processes the legendSection node
     */
    dvtm.processLegendSection = function(amxNode, legendSectionNode) {

        var data = amxNode["_dataObj"];
        var attrGroups = amxNode["_attributeGroups"];
        var agid;
        var ag;

        if (legendSectionNode.source) {
            agid = legendSectionNode.source;
            // find the corresponding attributeGroups info
            var g;
            for (g = 0; g < attrGroups.length; g++) {
                if (attrGroups[g].id === agid) {
                    ag = attrGroups[g];
                    break;
                }
            }
            // if the group could not be found by id, nothing to do here
            if (g >= attrGroups.length) {
                return;
            }

            // attribute group found, copy the info into the section legend
            var section = {title: legendSectionNode.title, items: []};
            for (var i = 0; i < ag.items.length; i++) {
                var markerColor = dvtm.getAttributeFromGroup("color", ag, i);
                var markerShape = dvtm.getAttributeFromGroup("shape", ag, i);
                // at the moment category and label are the same thing
                // TODO: text?
                //var item = {category: ag.items[i].value};
                var item = {id: ag.items[i].value};

                // attributeGroup has an optional 'label' attribute, if missing use 'category' as label
                if (ag.items[i].label != undefined)
                    item.text = ag.items[i].label;
                else
                    //item.text = item.category;
                    item.text = item.id;

                if (markerColor) {
                    item.color = markerColor;
                }
                if (markerShape) {
                    item.markerShape = markerShape;
                }
                section.items.push(item);
            }

            // add new legend section to the dataObject
            if (data.legend["sections"] == undefined) {
                data.legend["sections"] = [];
            }
            data.legend.sections.push(section);
        }

    }

    /**
     * applies resolved markers to the API model
     */
    dvtm.applyMarkersToModel = function(amxNode) {
        
        var markers = amxNode["_markers"];
        var chartType = dvtm.getChartType(amxNode);

        var i, j;
        var g, s;
        var ser;
        var color, shape;

        for (i = 0; i < markers.length; i++) {
            if (markers[i].groupId !== undefined) {
                dvtm.addGroup(amxNode, markers[i].groupId, markers[i].group);
            }
            else {
                dvtm.addGroup(amxNode, null, markers[i].group);
            }
        }

        // populate series with marker info
        for (i = 0; i < markers.length; i++) {
            var marker = markers[i];
            ser = dvtm.getSeriesByIdAndName(amxNode, marker.seriesId, marker.series);

            var dataItem = {};

            if (marker.x != undefined) {
                dataItem.x = marker.x;
            }
            if (marker.y != undefined) {
                dataItem.y = marker.y;
            }
            else if (marker.value != undefined) {
                dataItem.y = marker.value;
            }

            if (marker.z != undefined) {
                // bubble chart z value
                dataItem.z = marker.z;
            }
            if (marker.markerSize != undefined) {
                dataItem.markerSize = marker.markerSize;
            }
        
            if (marker.shortDesc != undefined) {
                dataItem.tooltip = marker.shortDesc;
            }
            if (marker.borderColor != undefined) {
                dataItem.borderColor = marker.borderColor;
            }

            // resolve attributes set via attributeGroups first
            if (marker.attrGroups.length > 0) {
                for (g = 0; g < marker.attrGroups.length; g++) {

                    var aGroup = marker.attrGroups[g].attrGroup;
                    var valueIndex = marker.attrGroups[g].valueIndex;

                    // resolve color
                    color = dvtm.getAttributeFromGroup("color", aGroup, valueIndex);
                    if (color != null) {
                        dataItem.color = color;
                    }

                    // resolve shapes
                    shape = dvtm.getAttributeFromGroup("shape", aGroup, valueIndex);
                    if (shape != null) {
                        dataItem.markerShape = shape;
                    }

                    // for bubble and scatter charts, populate the categories array
                    if (chartType === 'bubble' || chartType === 'scatter') {
                        if (dataItem.categories == undefined) {
                            dataItem.categories = [];
                        }

                        dataItem.categories.push(aGroup.items[valueIndex].value);
                    }

                }
            }

            // proceed with color and shape attributes set on marker tag
            if (marker.color != undefined) {
                dataItem.color = marker.color;
            }

            if (marker.markerShape != undefined) {
                dataItem.markerShape = marker.markerShape;
            }

            if (marker.markerDisplayed != undefined) {
                dataItem.markerDisplayed = (amx.isValueTrue(marker.markerDisplayed)) ? true : false;
            }
            
            // store the data item to the options series object
            ser.data.push(dataItem);
        }
        
    }

    /**
     * returns a reference to the series object.  First tries to find
     * the existing series by its id. If not found, creates a new series
     * object with the name and empty data array.
     */
    dvtm.getSeriesByIdAndName = function(amxNode, id, name) {

        var chartType = dvtm.getChartType(amxNode);
        var series = amxNode["_dataObj"].series;
        var s;
        var ser;

        // find existing series or create a new one
        for (s = 0; s < series.length; s++) {
            if (series[s].id === id)
                break;
        }
        if (s < series.length) {
            ser = series[s];
        }
        else {
            // for bubble and scatter charts, disable the series legend
            var showInLegend = 'on';
            if (chartType === 'bubble' || chartType === 'scatter')
                showInLegend = 'off';
            ser = {id: id, name: name, displayInLegend: showInLegend, data: []};
            series.push(ser);
        }
        return ser;
    }

    /**
     *  adds a new group to the groups array
     *  
     *  if groupId exists, the group is identified by groupId, and a new groups
     *  item is created as: {id: groupId, name: group}
     *  if groupId is missing, the group is identified by the 'group' parameter
     *  and the groups item is a plain string
     */
    dvtm.addGroup = function(amxNode, groupId, group) {
        var groups = amxNode["_dataObj"].groups;
        var g = 0;

        for (g = 0; g < groups.length; g++) {
            if (groupId) {
                if (groups[g].id === groupId)
                    return;
            }
            else {
                if (groups[g] === group)
                    return;
            }
        }
        if (g >= groups.length) {
            if (groupId) {
                groups.push({id: groupId, name: group});
            }
            else if (group !== undefined) {
                groups.push(group);
            }
        }
    }


    /**
     *
     */
    dvtm.getAttributeFromGroup = function(type, attrGroup, valueIndex) {

        // if the group provides the required type, resolve it
        // TODO: use a better way to check the type
        if (attrGroup.type.indexOf(type) >= 0) {
            switch (type) {
                case 'color':
                    return dvtm.defaultColors[valueIndex];

                case 'shape':
                    return dvtm.defaultShapes[valueIndex];
            }
        }

        return null;
    }

    /**
     *
     */
    dvtm.processSeriesStyle = function(amxNode, seriesStyleNode) {

        var series = amxNode["_dataObj"].series;

        // seriesStyle can be matched on seriesId or series, seriesId takes precedence, if present
        var seriesId;
        if (seriesStyleNode.seriesId) {
            seriesId = seriesStyleNode.seriesId;
        }
        else if (seriesStyleNode.series) {
            seriesId = seriesStyleNode.series;
        }
        else {
            // no id to match this seriesStyle on, exit
            return;
        }

        // find the series item to be updated
        var ser;
        var s = 0;
        for (s = 0; s < series.length; s++) {
            if (seriesId === series[s].id)
                break;
        }

        if (s < series.length) {
            ser = series[s];
        }
        else {
            // series not found by name, something's wrong
            return;
        }

        // do not apply the style, if 'rendered' is defined and evaluates to false
        if (seriesStyleNode.rendered !== undefined) {
            if (amx.isValueFalse(seriesStyleNode.rendered))
                return;
        }

        // set the series type for combo chart
        if (seriesStyleNode.type !== undefined) {
            ser.type = seriesStyleNode.type;
        }

        // set common series properties
        if (seriesStyleNode.color !== undefined) {
            ser.color = seriesStyleNode.color;
        }
        if (seriesStyleNode.borderColor !== undefined) {
            ser.borderColor = seriesStyleNode.borderColor;
        }
        if (seriesStyleNode.markerDisplayed !== undefined) {
            if (seriesStyleNode.markerDisplayed === "on"
             || amx.isValueTrue(seriesStyleNode.markerDisplayed))
                ser.markerDisplayed = true;
            else
                ser.markerDisplayed = false;
        }
        if (seriesStyleNode.markerShape !== undefined) {
            ser.markerShape = seriesStyleNode.markerShape;
        }
        if (seriesStyleNode.markerColor !== undefined) {
            ser.markerColor = seriesStyleNode.markerColor;
        }
        if (seriesStyleNode.markerSize !== undefined) {
            ser.markerSize = +seriesStyleNode.markerSize;
        }
        if (seriesStyleNode.lineWidth !== undefined) {
            ser.lineWidth = +seriesStyleNode.lineWidth;
        }
        if (seriesStyleNode.lineStyle !== undefined) {
            ser.lineStyle = seriesStyleNode.lineStyle;
        }
        if (seriesStyleNode.assignedToY2 !== undefined) {
            ser.assignedToY2 = seriesStyleNode.assignedToY2;
        }
    }
    
    /**
     * parses the sparkDataItem node attributes 
     * 
     * sparkDataItem has the following attributes
     * 
     *   color            - String(Color): support CSS color values
     *   date             - Number: ms since 1970/1/1
     *   floatValue       - Number: the float value
     *   markerDisplayed  - Boolean: should marker display
     *   rendered         - Boolean: should spark data item render
     *   value            - Number: the spark data item value
     *   
     */
    dvtm.processSparkDataItem = function(amxNode, sparkItem) {
        var data = amxNode["_dataObj"];
        var item = {};
        
        if (data.items == undefined) {
          data.items = [];
        }
        
        if (sparkItem.color)
        item.color = sparkItem.color;
        if (sparkItem.date) {
          item.date = sparkItem.date;
          data.timeAxisType = "enabled";
        }
        if (sparkItem.floatValue)
        item.floatValue = sparkItem.floatValue;
        if (sparkItem.markerDisplayed)
        item.markerDisplayed = (sparkItem.markerDisplayed === 'true' || sparkItem.markerDisplayed === true) ? true : false;
        if (sparkItem.rendered)
        item.rendered = (sparkItem.rendered === 'true' || sparkItem.rendered === true) ? true : false;
        if (sparkItem.value)
        item.value = sparkItem.value;
        
        data.items.push(item);
    }
    
    /**
     * parses the threshold node attributes 
     * 
     * threshold has the following attributes
     * 
     *   borderColor - String(Color): support CSS color values
     *   color       - String(Color): support CSS color values
     *   text        - String: the threshold text
     *   value       - Number: the breakpoint of the range
     *   
     */
    dvtm.processThreshold = function(amxNode, thresholdNode) {
        var data = amxNode["_dataObj"];
	var threshold = {};
        
        if (data.thresholds == undefined) {
          data.thresholds = [];
        }
        
        if (thresholdNode.borderColor)
          threshold.borderColor = thresholdNode.borderColor;
        if (thresholdNode.color)
          threshold.color = thresholdNode.color;
        if (thresholdNode.text)
          threshold.text = thresholdNode.text;
        if (thresholdNode.maxValue)
          threshold.maxValue = thresholdNode.maxValue;
          
        data.thresholds.push(threshold);
        
    }

    /**
     * Indicates whether the chart represented by the amxNode is a sparkChart
     */
    dvtm.isSparkChart = function(amxNode) {
        if (amxNode.tagName === "dvtm:sparkChart")
            return true;
        else
            return false;
    }

    /**
     * Indicates whether the chart represented by the amxNode is a gauge
     */
    dvtm.isGauge = function(amxNode) {
        if ((amxNode.tagName === "dvtm:ledGauge") || (amxNode.tagName === "dvtm:statusMeterGauge"))
            return true;
        else
            return false;
    }

    /**
     * Dumps the current state of the dataObject
     */
    dvtm.dumpDataObject = function(amxNode) {
        console.log(dvtm.dumpObj(amxNode["_dataObj"], "dataObject"));
    }

    /**
     * Dumps the current state of the dataObject
     */
    dvtm.dumpOptionsObject = function(amxNode) {
        console.log(dvtm.dumpObj(amxNode["_optionsObj"], "optionsObject"));
    }

    /**
     * dumps a JavaScript object
     */
    dvtm.dumpObj = function(obj, name, indent, depth) {

        if (indent === undefined) {
            indent = "";
        }
        if (depth === undefined) {
            depth = 0;
        }
        if (depth > 5) {
            return indent + name + ": <max dump depth exceeded!>\n";
        }
        if (typeof obj == "object") {
            var child = null;
            var output = indent + name + ": {\n";
            indent += "    ";
            for (var item in obj) {
                try {
                    child = obj[item];
                }
                catch (e) {
                    child = "<unable to evaluate>";
                }
                if (typeof child == "object") {
                    output += dvtm.dumpObj(child, item, indent, depth + 1);
                }
                else {
                    if (typeof child == "string") {
                        child = "\"" + child + "\"";
                    }
                    else if (child == null) {
                        child = "<null>";
                    }
                    output += indent + item + ": " + child + "\n";
                }
            }
            return output + indent + "}\n";
        }
        else {
            return obj;
        }
    }


    /**
     * Sets the flag indicating that the options object has been modified
     */
    dvtm.setOptionsDirty = function(amxNode, dirty) {
        amxNode["_optionsDirty"] = dirty;
    }

    /**
     * Indicates whether the options object has been modified
     */
    dvtm.isOptionsDirty = function(amxNode) {
        return amxNode["_optionsDirty"];
    }


})();


