// Titel:       charts
// Description: draw charts
// Copyright:   Copyright (c) 2010
// Company:     SiG Software Integration GmbH
// Author:      Dr. Horst Walther

//  --------------------------------------------------------------------------->
function Chart(p2d) {

  //  constants  ---------------------->
  var wordy = false;
  var RED = '#ff0000';
  var colors = new Array('black', 'blue', RED, 'green', 'pink', 'orange', 'yellow', 'magenta', 'cyan', 'white', 'lightgray', 'gray', 'darkgray'),
    Cross = 1,
    CrossDiag = 2,
    Square = 3,
    SquareFull = 4,
    Diamond = 5,
    Wye = 6,
    Star = 7,
    CIRCLE = 8,
    POINT = 9,
    NO = 0,
    SMALL = 1,
    ALL = 2,
    minVisWL = 380,
    maxVisWL = 780,
    visibleRange = 400;

  // variable Attributes  -------------->
  var xLen = 18.0;
  var yLen = 10.0;
  var sameChart = false;
  var autoScale = true;
  var regressionFlag = false;
  var fullRange = true;
  var isDown = false;

  var reg1 = 0,
    reg2 = 0,
    callCounter = 0,
    backgroundType = ALL,
    repeatCounter = 0;
  var xAxis = null,
    yAxis = null,
    animatedSymbol = null,
    animatedHorizLine = null,
    animatedVertLine = null,
    animatedTextX = null,
    animatedTextY = null,
    animatedTextXnode = null,
    animatedTextYnode = null;
  var clipFromLine = null, 
    clipToLine = null;


  var node = null,
    regressionNode = null,
    extrema = new Extrema(),
    saveExtrema = new Extrema(),
    regressionLine = null,
    colorScaler = new Scaler();
  var nodes = [];
  var xMin = 0;
  var xMax = 0;

  this.id = 'chart';
  this.backgroundColor = 'white';
  this.chartColor = 'black';
  this.lineColor = 'blue';
  this.gridColor = 'gray';
  this.symbolColor = RED;
  this.regressionColor = RED;
  this.autoColor = false;
  this.isRainbow = false,
  this.isExtRainbow = false,
  this.origin = new Point2d(1.5, 8.0);
  this.factor = 1.0;
  this.x = [];
  this.y = [];
  this.n = this.y.length;
  this.xByZero = false;
  this.yByZero = false;
  this.titel = '';
  this.xText = 'x';
  this.yText = 'y';
  this.symbolType = 9;
  this.symbolSize = 3;
  this.lineType = 1;
  this.xGridFlag = false;
  this.yGridFlag = false;
  this.animationId = null;
  this.animationContainer = null;
  this.animationCounter = 0;
  this.clipArea = null;

  //  -------------------------------------------------------------------------
  this.init = function (x, y, n, xByZero, yByZero, titel, xText, yText, symbolType, symbolSize, lineType, xGridFlag, yGridFlag) {
//    alert (this.id+'.init ('+x+', '+y+', '+n+', '+xByZero+', '+yByZero+', '+titel+', '+xText+', '+yText+', '+symbolType+', '+symbolSize+', '+lineType+', '+xGridFlag+', '+yGridFlag+')');
    this.id = p2d.getUniqueID('chart');
    document.chart = this;
    if (!y.length) return this;
    this.y = y;
    if (!x) {
      this.x = new Array(y.length);
      for (var i = 0; i < y.length; i++)
        this.x[i] = i;
    } else
       this.x = x;
    this.n = n ? Math.min(n, x.length) : Math.min(x.length, y.length);
    this.xGridFlag  = xGridFlag  == undefined ? false : xGridFlag;
    this.yGridFlag  = yGridFlag  == undefined ? false : yGridFlag;
    this.symbolType = symbolType == undefined ? 9 : symbolType;
    this.symbolSize = symbolSize == undefined ? 3 : symbolSize;
    this.lineType   = lineType   == undefined ? 1 : lineType;
    if (!titel) this.titel = '';
    if (!xText) this.xText = 'x';
    if (!yText) this.yText = 'y';
    this.xByZero = xByZero == undefined ? false : xByZero;
    this.yByZero = yByZero == undefined ? false : yByZero; /**/
    // alert ('Chart.init: '+this.toString ());
    this.getBackGroundNode().onmousedown = this.down;
    return this;
  }

  //  -------------------------------------------------------------------------
  this.set = function (x, y, n, xByZero, yByZero, titel, xText, yText, symbolType, symbolSize, lineType, xGridFlag, yGridFlag) {
//    alert (this.id+'.set ('+x+', '+y+', '+n+', '+xByZero+', '+yByZero+', '+titel+', '+xText+', '+yText+', '+symbolType+', '+symbolSize+', '+lineType+', '+xGridFlag+', '+yGridFlag+')');
    this.id = p2d.getUniqueID('chart');
    document.chart = this;
    if (x) this.x = x;
    if (y) this.y = y;
    if (n) this.n = n;
    this.y = y;
    if (!this.x) {
      this.x = [];
      for (var i = 0; i < this.y.length; i++)
        this.x[i] = i;
    }
    this.n = n ? Math.min(n, x.length) : Math.min(x.length, y.length);
    this.xByZero = xByZero == undefined ? false : xByZero;
    this.yByZero = yByZero == undefined ? false : yByZero; /**/
    this.titel = titel   == undefined ? this.titel : titel;
    this.xText = xText   == undefined ? this.xText : xText;
    this.yText = yText   == undefined ? this.yText : yText;
    this.symbolType = symbolType == undefined ? this.symbolType : symbolType;
    this.symbolSize = symbolSize == undefined ? this.symbolSize : symbolSize;
    this.lineType   = lineType   == undefined ? this.lineType : lineType;
    this.xGridFlag  = xGridFlag  == undefined ? this.xGridFlag : xGridFlag;
    this.yGridFlag  = yGridFlag  == undefined ? this.yGridFlag : yGridFlag;
    this.getBackGroundNode().onmousedown = this.down;
    return this;
  }

  //  --------------------------------------------------------------------------->
  this.toString = function () {
    return this.id+'=' + this.x.length + ', ' + this.y.length + ', ' + this.n + ', ' + this.xByZero + ', ' + this.yByZero + ', ' + this.titel + ', ' + this.xText + ', ' + this.yText + ', ' + this.symbolType + ', ' + this.symbolSize + ', ' + this.lineType + ', ' + this.xGridFlag + ', ' + this.yGridFlag + ')';
  }

//  -------------------------------------------------------------------------->
  this.getWorldGraphics = function () {
    return p2d; 
  };
  //  --------------------------------------------------------------------------->
  this.getContainer = function () {
    return node;
  };

  //  --------------------------------------------------------------------------->
  this.setBackgroundType = function (type) {
    backgroundType = type;
  };

  //  --------------------------------------------------------------------------->
  this.setSame = function (sameChart) {
    this.sameChart = sameChart;
    if (!sameChart) callCounter = 0;
  };

  //  --------------------------------------------------------------------------->
  this.setFactor = function (factor) {
//    alert (this.id+'.setFactor ('+factor+')');
    this.factor = factor;
    node = p2d.scale(this.id, this.factor);
    return this;
  };

  //  --------------------------------------------------------------------------->
  this.getFactor = function () {
    return this.factor;
  };

  //  --------------------------------------------------------------------------->
  this.setAutoScale = function (b) {
    this.autoScale = b;
  };

  //  --------------------------------------------------------------------------->
  this.setManualScale = function (xMin, yMin, xMax, yMax) {
    this.autoScale = false;
    extrema.assignValues(xMin, yMin, xMax, yMax);
  }

  //  --------------------------------------------------------------------------->
  this.setAutoFit = function (d) {
    var size = getSize();
    p2d.setFactor(Math.min(d.width / size.width, d.height / size.height));
    if (wordy) alert("window= (" + d.width + " / " + d.height + ") , graph=" + size.width + " / " + size.height + ") , factor=" + (Math.min(d.width / size.width, d.height / size.height)));
  }

  //  --------------------------------------------------------------------------->
  this.setOrigin = function (o) {
    this.origin = o;
  }

  //  --------------------------------------------------------------------------->
  this.getOrigin = function () {
    return this.origin;
  }

  //  --------------------------------------------------------------------------->
  this.getExtrema = function () {
    return this.extrema;
  }

  //  --------------------------------------------------------------------------->
  this.setExtrema = function (x, y) {
    this.extrema.find(x, y);
  }

  //  --------------------------------------------------------------------------->
  this.getSaveExtrema = function () {
    return this.saveExtrema;
  }

  //  --------------------------------------------------------------------------->
  this.setSaveExtrema = function (x, y) {
    saveExtrema.find(x, y);
  }

  //  --------------------------------------------------------------------------->
  this.expandExtrema = function (x, y) {
    return this.extrema.expand(x, y);
  }

  //  --------------------------------------------------------------------------->
  this.getSize = function () {
    if (wordy) alert("getSize ()=p2d.toScreenX (" + (2 * this.origin.x) + " + " + xLen + ") --> " + p2d.toScreenX(2 * this.origin.x + xLen) + "), p2d.toScreenY (" + (2 * this.origin.y) + " + " + yLen + ") --> " + p2d.toScreenY(2 * this.origin.y + yLen));
    return new Dimension(p2d.toScreenX(2 * this.origin.x + xLen), p2d.toScreenY(2 * this.origin.y + yLen));
  }

  //  --------------------------------------------------------------------------->
  this.getDrawAreaMinX = function () {
    return this.origin.x;
  }

  //  --------------------------------------------------------------------------->
  this.getDrawAreaMinY = function () {
    return this.origin.y;
  }

  //  --------------------------------------------------------------------------->
  this.getDrawAreaMaxX = function () {
    return this.origin.x + xLen;
  }

  //  --------------------------------------------------------------------------->
  this.getDrawAreaMaxY = function () {
    return this.origin.y - yLen;
  }

  //  --------------------------------------------------------------------------->
  this.getTitel = function () {
    return titel;
  }

  //  --------------------------------------------------------------------------->
  this.getSame = function () {
    return sameChart;
  }

  //  --------------------------------------------------------------------------->
  this.setBackGroundColor = function (color) {
    this.backgroundColor = color;
    return p2d.setBackGroundColor(color);
  };

  //  --------------------------------------------------------------------------->
  this.getBackgroundColor = function () {
    return backgroundColor;
  }

  //  --------------------------------------------------------------------------->
  this.setChartColor = function (c) {
    this.chartColor = c;
    return this;
  }

  //  --------------------------------------------------------------------------->
  this.getChartColor = function () {
    return this.chartColor;
  }

  //  --------------------------------------------------------------------------->
  this.setColors = function (backgroundColor, chartColor, lineColor, symbolColor, gridColor) {
//    alert (this.id+'.setColors ('+backgroundColor+', '+chartColor+', '+lineColor+', '+symbolColor+', '+gridColor+')');
    this.setBackGroundColor (backgroundColor);
    this.chartColor = chartColor;
    this.isRainbow = lineColor == 'rainbow' ? true : false;
    this.lineColor = this.isRainbow ? this.lineColor : lineColor;
    this.symbolColor = symbolColor;
    this.gridColor = gridColor;
    return this;
  }

  //  --------------------------------------------------------------------------->
  this.setRainbowColors = function (min, max) {
//    alert(this.id + ".setRainbowColors (" + min + ", " + max + ")");
    if (this.isExtRainbow) return this;
    if (min == max) return this;
    if (colorScaler == null) colorScaler = new Scaler();
    return colorScaler.autoScale(min, max, visibleRange); // 780 nm - 380 nm = 400
  }

  //  --------------------------------------------------------------------------->
  this.setToRainbow = function () {
    this.isRainbow = true;
    this.isExtRainbow = false;
    return this;
  }

  //  --------------------------------------------------------------------------->
  this.setExtRainbow = function (cs) {
    this.setToRainbow();
    if (cs != null) {
      this.colorScaler = cs;
      this.isExtRainbow = true;
    }
    else this.isExtRainbow = false;
    return this;
  }

  //  --------------------------------------------------------------------------->
  this.getColorScaler = function () {
    return colorScaler;
  }

  //  --------------------------------------------------------------------------->
  this.setAxisLen = function (x, y) {
    this.xLen = x;
    this.yLen = y;
    return this;
  }

  //  --------------------------------------------------------------------------->
  this.getAxisLen = function () {
    return new Point2d(this.xLen, this.yLen);
  }

  //  --------------------------------------------------------------------------->
  this.setDateAxis = function (b) {
    this.dateAxis = b;
    return this;
  }

  //  --------------------------------------------------------------------------->
  this.getNumPoints = function () {
    return n;
  }

  //  --------------------------------------------------------------------------->
  this.getX = function () {
    return x;
  }

  //  --------------------------------------------------------------------------->
  this.getY = function () {
    return y;
  }

  //  drawing methods  -----------------------------------------------------------
  this.drawSpecialSymbols = function (iSpecial, specialSymbolSize, specialSymbolType, color) {
    var wordy = false;
    if (wordy) {
      alert(iSpecial.length + " : ");
      for (var i = 0; i < iSpecial.length; i++)
      alert("Punkt [" + iSpecial[i] + "] = (" + x[iSpecial[i]] + ", " + y[iSpecial[i]] + ")");
    }
    color = autoColor ? colors[Math.max(0, sameChart ? callCounter % colors.length : callCounter + 2)] : color;
    for (var i = 0; i < iSpecial.length; i++)
    this.add (p2d.drawSymbol(xAxis.scaleValue(x[iSpecial[i]]), yAxis.scaleValue(y[iSpecial[i]]), specialSymbolSize, specialSymbolType, color));
    return this;
  }

  //  --------------------------------------------------------------------------->
  this.drawSpecialSymbolsX = function (xSpecial, specialSymbolType, specialSymbolSize, color) {
    color = autoColor ? colors[Math.max(0, sameChart ? callCounter % colors.length : callCounter + 2)] : color;
    for (var i = 0; i < xSpecial.length; i++)
      this.add (p2d.drawSymbol(xAxis.scaleValue(xSpecial[i]), yAxis.scaleValue(0.0), specialSymbolSize, specialSymbolType, color));
    return this;
  }

  //  --------------------------------------------------------------------------->
  this.drawLine = function (x1, y1, x2, y2, lineType, stroke, color, opacity) {
    return this.add (p2d.drawLine(xAxis.scaleValue(x1), yAxis.scaleValue(y1), xAxis.scaleValue(x2), yAxis.scaleValue(y2), lineType, 1, color, 1.0));
  }

  //  --------------------------------------------------------------------------->
  this.drawSymbol = function (x, y, symbolType, symbolSize, stroke, color, opacity) {
    return this.add (p2d.drawSymbol(xAxis.scaleValue(x), yAxis.scaleValue(y), symbolType, symbolSize, stroke, color, opacity));
  }

  //  --------------------------------------------------------------------------->
  this.changeSymbol = function (node, x, y, symbolType, symbolSize, stroke, color, opacity) {
    return p2d.changeSymbol(node, xAxis.scaleValue(x), yAxis.scaleValue(y), symbolType, symbolSize, stroke, color, opacity);
  }

  //  --------------------------------------------------------------------------->
  this.drawAnimatedSymbols = function (color) {
//    alert('drawAnimatedSymbols: '+this.animationCounter+' <> '+this.n);
    var xi = this.x[this.animationCounter];
    var yi = this.y[this.animationCounter];
    if (this.animationCounter) {
      p2d.enterContainer(this.animationContainer);
      animatedSymbol = this.changeSymbol(animatedSymbol, xi, yi, POINT, 5, 1, color, 1.0);
      animatedHorizLine = this.changeHorizLine(animatedHorizLine, xi, yi, Math.max (xAxis.getValMin(), 0.0));
      animatedVertLine  = this.changeVertLine (animatedVertLine,  xi, yi, Math.max (yAxis.getValMin(), 0.0));
      animatedTextX = xAxis.changeCoordText(animatedTextX, animatedTextXnode, xi, yi, color, 'middle'); 
      animatedTextY = yAxis.changeCoordText(animatedTextY, animatedTextYnode, xi, yi, color, xi < 0 ? 'start' : 'end');
    } else {
//      alert('drawAnimatedSymbols('+this.n+'): x['+this.animationCounter+']='+this.x[this.animationCounter]+', y['+this.animationCounter+']='+this.y[this.animationCounter]);
      this.animationContainer = p2d.makeContainer ('animation');
      animatedSymbol = this.add(this.drawSymbol(xi, yi, POINT, 5, 1, color, 1.0));
      animatedHorizLine = this.drawHorizLine(xi, yi, Math.max (xAxis.getValMin(), 0.0), color);
      animatedVertLine  = this.drawVertLine (xi, yi, Math.max (yAxis.getValMin(), 0.0), color);
      animatedTextX = xAxis.makeCoordText(xi, yi, color, 'middle');
//      alert ('animatedTextX='+animatedTextX);
      animatedTextXnode = this.add(animatedTextX.draw());
      animatedTextY = yAxis.makeCoordText(xi, yi, color, xi < 0 ? 'start' : 'end');
//      alert ('animatedTextY='+animatedTextY);
      animatedTextYnode = this.add(animatedTextY.draw());
    }
    this.animationCounter++;
    if (this.animationCounter < this.n) return;
    else {
      clearInterval(this.animationId);
      this.animationCounter = 0;
      p2d.removeNode(animatedSymbol);
      p2d.removeNode(animatedHorizLine);
      p2d.removeNode(animatedVertLine);
      p2d.removeNode(animatedTextXnode);
      p2d.removeNode(animatedTextYnode);
    }
    p2d.leaveContainer();
  };

  //  --------------------------------------------------------------------------->
  this.setColor = function (color) {
    return this.autoColor ? colors[Math.max(repeatCounter, sameChart ? callCounter % colors.length : callCounter + 1)] : color;
  };

  //  --------------------------------------------------------------------------->
  this.setRainbowColor = function (i) {
    var color = p2d.toColor((colorScaler.scaleValue(this.y[i]) + minVisWL));
    return Color(color[0],color[1], color[2]);
  };

  //  --------------------------------------------------------------------------->
  this.drawSymbols = function (color) {
    for (var i = 0; i < n; i++) {
      this.add (this.drawSymbol(this.x[i], this.y[i], this.symbolType, this.symbolSize, 1, color, 1.0));}
  }

  //  --------------------------------------------------------------------------->
  this.drawLinesAndSymbols = function (color) {
//    alert (this.id+'.drawLinesAndSymbols ('+color+'), this.isRainbow='+this.isRainbow);
    if (this.clipArea) this.clipArea.setBounds (this.xAxis, this.yAxis);
    color = this.setColor(color);
    p2d.makeContainer ('LinesAndSymbols');
    for (var i = 0; i < this.n; i++) {
      if (this.isRainbow) color = this.setRainbowColor(i);
      if (this.lineType && i && this.inBounds (this.x[i-1]) && this.inBounds (this.x[i])) this.add (this.drawLine  (this.x[i - 1], this.y[i - 1], this.x[i], this.y[i], this.lineType, 1, color, 1.0));
      if (this.symbolType && this.inBounds (this.x[i])) this.add (this.drawSymbol(this.x[i], this.y[i], this.symbolType, this.symbolSize, 1, color, 1.0));
    }
    p2d.leaveContainer();
    return this;
  }

  //  --------------------------------------------------------------------------->
  this.drawHorizLine = function (x1, y, x2, color) {
    if (xAxis == null || yAxis == null) return;
    return this.add (p2d.drawLine(xAxis.scaleValue(x1), yAxis.scaleValue(y), yAxis.getZero().x, yAxis.scaleValue(y), this.lineType, 1, color, 1.0));
  }

  //  --------------------------------------------------------------------------->
  this.drawVertLine = function (x, y1, y2, color) {
    return this.add (p2d.drawLine(xAxis.scaleValue(x), yAxis.scaleValue(y1), xAxis.scaleValue(x), yAxis.scaleValue(y2), this.lineType, 1, color, 1.0));
  }

  //  --------------------------------------------------------------------------->
  this.changeHorizLine = function (node, x, y, x2, color) {
    return p2d.changeLine(node, xAxis.scaleValue(x), yAxis.scaleValue(y), yAxis.getZero().x, yAxis.scaleValue(y), null, null, color, null);
  }

  //  --------------------------------------------------------------------------->
  this.changeVertLine = function (node, x, y1, y2, color) {
    return p2d.changeLine(node, xAxis.scaleValue(x), yAxis.scaleValue(y1), xAxis.scaleValue(x), yAxis.scaleValue(y2), null, null, color, null);
  }

  //  --------------------------------------------------------------------------->
  this.setRegressionLine = function (c, i1, i2, fullRange) {
    if (this.n <= 1) return;
    if (!c) c = RED;
    if (!i1 && !i2) fullRange = true;
    this.fullRange = fullRange;
    this.reg1 = i1 ? i1 : 0;
    this.reg2 = i2 ? i2 : this.n - 1;
    this.regressionFlag = true;
    this.regressionColor = c;
  }

  //  --------------------------------------------------------------------------->
  this.resetRegressionLine = function () {
    this.regressionFlag = false;
    p2d.removeNode(regressionNode);
  }

  //  --------------------------------------------------------------------------->
  this.makeRegressionLine = function (x, y) {
    regressionFlag = !regressionFlag;
    if (regressionFlag) return new RegressionLine(x, y)
    else this.resetRegressionLine();
    return null;
  }

  //  --------------------------------------------------------------------------->
  this.drawRegressionLine = function () { //    alert ("now draw the regression line ...: "+n+" points: ("+ xAxis.getOrigin ().x+", "+ yAxis.scaleValue (regressionLine.calc (xAxis.getValMin ()))+") - ("+ (xAxis.getOrigin ().x + xLen)+", "+ yAxis.scaleValue (regressionLine.calc (xAxis.getValMax ()))+")");
    regressionLine = this.makeRegressionLine(this.x, this.y); //    show ('trace', regressionLine.toString());
    if (!regressionLine) return false;
    if (this.n < 2) return false;
    p2d.enterContainer(this.getContainer());
    var x1 = y1 = x2 = y2 = 0;
    if (fullRange) {
      x1 = xAxis.getOrigin().x;
      y1 = yAxis.scaleValue(regressionLine.calc(xAxis.getValMin()));
      x2 = xAxis.getOrigin().x + xLen;
      y2 = yAxis.scaleValue(regressionLine.calc(xAxis.getValMax()));
    } else {
      x1 = xAxis.scaleValue(x[reg1]);
      y1 = yAxis.scaleValue(regressionLine.calc(x[reg1]));
      x2 = xAxis.scaleValue(x[reg2]);
      y2 = yAxis.scaleValue(regressionLine.calc(x[reg2]));
    }
    regressionNode = this.add (p2d.drawLine(x1, y1, x2, y2, this.lineType, 1, this.regressionColor, 1.0));
    p2d.leaveContainer();
  }

  //  ---------------------------------------------------------------------------> 
  this.ClipArea = function (xMin, xMax, yMin, yMax) {
  
    //  ---------------------------------------------------------------------------> 
    this.toString = function () {
      return 'clip area= x: '+this.xMin+' - '+this.xMax+', y: '+this.yMin+' - '+this.yMax;
    };
  
    //  ---------------------------------------------------------------------------> 
    this.set = function (xMin, xMax, yMin, yMax) {
      this.xMin = xMin ? xMin : this.xMin;
      this.xMax = xMax ? xMax : this.xMax; 
      this.yMin = yMin ? yMin : this.yMin; 
      this.yMax = yMax ? yMax : this.yMax;
    };

    //  ---------------------------------------------------------------------------> 
    this.inBounds = function (x) {
      x = this.xAxis.scaleValue(x);
      return (x >= this.xMinWorld) && (x <= this.xMaxWorld);  //  -->
    };

    //  ---------------------------------------------------------------------------> 
    this.setBounds = function (xAxis, yAxis) {
      this.xAxis = xAxis;
      this.yAxis = yAxis;
      if (this.xMin) this.xMinWorld = this.xAxis.scaleValue(this.xMin);
      if (this.xMax) this.xMaxWorld = this.xAxis.scaleValue(this.xMax);
      if (this.yMin) this.yMinWorld = this.yAxis.scaleValue(this.yMin);
      if (this.yMax) this.yMaxWorld = this.yAxis.scaleValue(this.yMax);
    };

    this.set (xMin, xMax, yMin, yMax);
  };


  //  ---------------------------------------------------------------------------> 
  this.inBounds = function (x) {
    return this.clipArea ? this.clipArea.inBounds(x) : true;
  };

  //  ---------------------------------------------------------------------------> 
  this.setXaxis = function () {
//   if (this.clipArea) alert (this.id+'.setXaxis(): extrema='+extrema.getMin().x+', '+extrema.getMax().x+', clip range='+this.clipFromLine.xMin+', '+this.clipToLine.xMax+', clipArea='+this.clipArea);
    var xMin = this.clipArea ? this.clipArea.xMin : extrema.getMin().x;
    var xMax = this.clipArea ? this.clipArea.xMax : extrema.getMax().x;
    var axisType = isDate(this.x[0]) ? 'dateTime' : 'number';
    if (this.xAxis && this.xAxis.isSameType (axisType)) {
      return this.xAxis.remove().setShift(0).set(this.origin, xMin, xMax, xLen, (this.xGridFlag ? yLen : 0.0), true, this.xText, this.chartColor);}
    else {
      this.xAxis = isDate(this.x[0]) ? new DateTimeAxis(p2d) : new Axis(p2d);
      return this.xAxis.init(this.origin, xMin, xMax, xLen, (this.xGridFlag ? yLen : 0.0), true, this.xText, this.chartColor);
    }
  }

  //  --------------------------------------------------------------------------->
  this.setYaxis = function () { 
//    if (wordy) alert (this.id+'.setYaxis()');
    var yMin = extrema.getMin().y;
    var yMax = extrema.getMax().y;
    if (this.yAxis) {
      return this.yAxis.remove().setShift(0).set(this.origin, yMin, yMax, yLen, (this.yGridFlag ? xLen : 0.0), false, this.yText, this.chartColor);} 
    else 
      return (new Axis(p2d)).init(this.origin, yMin, yMax, yLen, (this.yGridFlag ? xLen : 0.0), false, this.yText, this.chartColor);
  };

  //  --------------------------------------------------------------------------->
  this.getAxes = function() {
    var axes = [];
    axes ['x'] = xAxis;
    axes ['y'] = yAxis;
//    axes ['z'] = zAxis;
    return axes;
  };

  //  --------------------------------------------------------------------------->
  this.check = function () {
    if (!this.x || (this.x.length < 2)) return false;
    if (!this.y || (this.y.length < 2)) return false;
    if (!this.n) this.n = this.y.length;
    return true;
  };

  //  --------------------------------------------------------------------------->
  this.draw = function () {
    var wordy = false;
    if (!this.check) { 
      alert(this.id+".draw: ###  error: Chart has " + this.n + " elements");
      return this;
    }
    if (wordy) alert("Chart.draw : run #" + callCounter + " (same chart = " + sameChart + ")");
    this.enterContainer();
    if (sameChart && callCounter) {
      if (wordy) alert(" (sameChart && (callCounter > 0)) ...");
      lineColor = autoColor ? colors[Math.max(0, sameChart ? (callCounter % colors.length) : callCounter)] : lineColor;
      if (symbolType) nodes [nodes.length] = this.add (p2d.drawSymbol((xAxis.getOrigin().x + xLen + 0.2), (xAxis.getOrigin().y + yLen - callCounter * 0.5 + 0.1), symbolSize, symbolType, symbolColor));
      if (lineType) nodes [nodes.length] = this.add (p2d.drawLine((xAxis.getOrigin().x + xLen + 0.5), (xAxis.getOrigin().y + yLen - callCounter * 0.5 - 0.2), (xAxis.getOrigin().x + xLen + 5.0), (xAxis.getOrigin().y + yLen - callCounter * 0.5 - 0.2), lineType, 1, lineColor, 1.0));
      nodes [nodes.length] = new HWText(p2d, titel, (xAxis.getOrigin().x + xLen + 1.5), (xAxis.getOrigin().y + yLen - callCounter * 0.5), 18, chartColor).draw('middle');
    } else { //      alert ('draw into new chart!');
      if (autoScale) extrema.find(this.x, this.y);
      if (this.isRainbow) this.setRainbowColors(extrema.getYmin(), extrema.getYmax());
      if (!this.check()) {
        if (wordy) alert(this.id+".draw: " + this.titel + " extrema check failed: "+this.x.length + ", " + this.y.length + ", n=" + this.n + ", " + extrema.getMax().x + ", " + extrema.getMin().x + ", " + extrema.getMax().y + ", " + extrema.getMin().y);
        return this;
      }
      if (wordy) alert (this.id+'.draw().extrema: '+ extrema.getMin().x+', '+extrema.getMax().x);
      xAxis = this.setXaxis(); nodes [nodes.length] = xAxis;
      yAxis = this.setYaxis(); nodes [nodes.length] = yAxis;
      if (this.yByZero && ((extrema.getMin().x <= 0.0) && (extrema.getMax().x >= 0.0))) yAxis.setShift(xAxis.getScaler().scaleValue(0.0)); // schneidet Y-Achse bei 0
      if (this.xByZero && ((extrema.getMin().y <= 0.0) && (extrema.getMax().y >= 0.0))) xAxis.setShift(yAxis.getScaler().scaleValue(0.0)); // schneidet X-Achse bei 0
      xAxis.setGridColor(this.gridColor).draw();
      yAxis.setGridColor(this.gridColor).draw();
      nodes [nodes.length] = this.add (new HWText(p2d, this.titel, xAxis.getOrigin().x + xLen * 0.5, xAxis.getOrigin().y + yLen + 1.2, 18, this.chartColor).draw('middle'));
    }
    this.drawLinesAndSymbols(this.lineColor);
    if (regressionFlag) this.drawRegressionLine();
    if (sameChart) callCounter %= colors.length;
    callCounter++;
    this.leaveContainer();
    return this;
  };

  //  --------------------------------------------------------------------------->
  this.enterContainer = function () {
    if (node)
      p2d.enterContainer(node);
    else
      node = p2d.makeContainer(this.id);
//    node = p2d.scale(this.id, this.factor);
    return this;
  };

  //  ----------------------------------------------------------------------------
  this.leaveContainer = function () {
    return p2d.leaveContainer();
  };

  //  ----------------------------------------------------------------------------
  this.remove = function () {
    if (!node) return null;
    p2d.removeNode(node);
    node = null;
    return this;
  };

  //  ----------------------------------------------------------------------------
  this.showAxes = function (showX, showY) {
    this.enterContainer ();
    if (showX) xAxis.draw();
    else xAxis.remove();
    if (showY) yAxis.draw();
    else yAxis.remove();
    this.leaveContainer ();
    return this;
  };

  //  --------------------------------------------------------------------------->
  this.add = function (node) {
    return p2d.add(node);
  };

  //  ----------------------------------------------------------------------------
  this.getBackGroundNode = function () {
    return p2d.getBackGroundNode();
  };

  //  ----------------------------------------------------------------------------
  this.down = function (evt) {
    if (isDown) return;
    isDown = true;	
    document.onmousemove = document.chart.move;
    document.onmouseup = document.chart.up;
    var x = fixEvent(evt).clientX;
    var pixelMinX = document.chart.getAxes().x.toPixels (document.chart.getAxes().x.getMin());
    var pixelMaxX = document.chart.getAxes().x.toPixels (document.chart.getAxes().x.getMax());
    var pixelMinY = document.chart.getAxes().y.toPixels (document.chart.getAxes().y.getMin());
    var pixelMaxY = document.chart.getAxes().y.toPixels (document.chart.getAxes().y.getMax());
    var tx = document.chart.getWorldGraphics().truePixelX(x);/*
    document.chart.getWorldGraphics().getGraphics().drawLine(pixelMinX, pixelMinY, pixelMinX, pixelMaxY);
    document.chart.getWorldGraphics().getGraphics().drawLine(pixelMinX, pixelMaxY, pixelMaxX, pixelMaxY);
    document.chart.getWorldGraphics().getGraphics().drawLine(pixelMaxX, pixelMaxY, pixelMaxX, pixelMinY);
    document.chart.getWorldGraphics().getGraphics().drawLine(pixelMaxX, pixelMinY, pixelMinX, pixelMinY);
    alert (pixelMinX+'<='+tx+' <= '+ pixelMaxX); // --> */
    if ((tx >= pixelMinX) && (tx <= pixelMaxX)) { // -->
      document.chartNode.saveForm ();
      if (document.chart.clipFromLine) document.chart.getWorldGraphics().removeNode(document.chart.clipFromLine);
      if (document.chart.clipToLine  ) document.chart.getWorldGraphics().removeNode(document.chart.clipToLine);
      var xWorld = p2d.toWorldX(x);
      var xMin = xAxis.rebuildValue(x);
      document.chart.clipArea = new document.chart.ClipArea (xMin, xMin);
      document.chart.clipFromLine = yAxis.drawParallel (xWorld, 'red');
      document.chart.clipToLine   = yAxis.drawParallel (xWorld, 'red');
      document.chartNode.updateForm (xMin);
    } else {
      document.onmousemove = null;
      document.onmouseup = null;
      document.chart.getWorldGraphics().removeNode(document.chart.clipFromLine);
      document.chart.getWorldGraphics().removeNode(document.chart.clipToLine);
      document.chart.clipArea = null;
      document.chartNode.restoreForm ();
      document.chartNode.updateFunction();
      document.chart.getBackGroundNode().onmousedown = document.chart.down;
    }
  };

  //  ----------------------------------------------------------------------------
  this.move = function (evt) {
    if (!isDown) return;
    var x = fixEvent(evt).clientX;
    var xWorld = p2d.toWorldX(x);
    var xMax = xAxis.rebuildValue(x);
    document.chart.clipToLine = p2d.changeLine (document.chart.clipToLine, xWorld, null, xWorld, null);
    document.chartNode.updateForm (null, xMax);
  };

  //  ----------------------------------------------------------------------------
  this.up = function (evt) {
    if (!isDown) return;
    isDown = false;
    document.onmousemove = null;
    document.onmouseup = null;
    if(!document.chart.clipArea) return;
    var x = fixEvent(evt).clientX;
    var xMin = document.chart.clipArea.xMin;
    var xMax = document.chart.getAxes().x.rebuildValue(x);
    document.chart.clipArea.set (null, xMax);
    var xWorldMax2 = document.chart.getWorldGraphics().toWorldX(x);
    document.chartNode.updateFunction();
    var xWorldMin = document.chart.getAxes().x.scaleValue(xMin);
    var xWorldMax = document.chart.getAxes().x.scaleValue(xMax);
    document.chart.clipFromLine = p2d.changeLine (document.chart.clipFromLine, xWorldMin, null, xWorldMin, null);
    document.chart.clipToLine   = p2d.changeLine (document.chart.clipToLine  , xWorldMax, null, xWorldMax, null);
  };
  //  --------------------------------------------------------------------------->
}
