text: var Kline = function (option) {
  
      this.element = "#kline_container";
      this.chartMgr = null;
      this.G_HTTP_REQUEST = null;
      this.timer = null;
      this.buttonDown = false;
      this.init = false;
      this.requestParam = "";
      this.data = {};
      this.width = 100+'%';
      this.height = 462;
      this.symbol = "";
      this.symbolName = "";
      this.range = null;
      this.url = "";
      this.limit = 1000;
      this.type = "poll";
      this.subscribePath = "";
      this.sendPath = "";
      this.socketClient = null;
      this.intervalTime = 5000;
      this.debug = true;
      this.language = "zh-cn";
      this.theme = "dark";
      this.ranges = ["1w", "1d", "1h", "30m", "15m", "5m", "1m", "line"];
      this.showTrade = true;
      this.tradeWidth = 250;
      this.socketConnected = false;
      this.enableSockjs = true;
      this.reverseColor = false;
  
      this.periodMap = {
          "01w": 7 * 86400 * 1000,
          "03d": 3 * 86400 * 1000,
          "01d": 86400 * 1000,
          "12h": 12 * 3600 * 1000,
          "06h": 6 * 3600 * 1000,
          "04h": 4 * 3600 * 1000,
          "02h": 2 * 3600 * 1000,
          "01h": 3600 * 1000,
          "30m": 30 * 60 * 1000,
          "15m": 15 * 60 * 1000,
          "05m": 5 * 60 * 1000,
          "03m": 3 * 60 * 1000,
          "01m": 60 * 1000,
          "line": 60 * 1000
      };
  
      this.tagMapPeriod = {
          "1w": "01w",
          "3d": "03d",
          "1d": "01d",
          "12h": "12h",
          "6h": "06h",
          "4h": "04h",
          "2h": "02h",
          "1h": "01h",
          "30m": "30m",
          "15m": "15m",
          "5m": "05m",
          "3m": "03m",
          "1m": "01m",
          "line": "line"
      };
  
      Object.assign(this, option);
  };
  
  Kline.prototype = {
  
      /*********************************************
       * Methods
       *********************************************/
  
      draw: function () {
          draw(this);
      },
  
      resize: function (width, height) {
          this.width = width;
          this.height = height;
          on_size(this.width, this.height);
      },
  
      setSymbol: function (symbol, symbolName) {
          KlineIns.symbol = symbol;
          KlineIns.symbolName = symbolName;
          switch_symbol(symbol);
          KlineIns.onSymbolChange(symbol, symbolName);
      },
  
      setTheme: function (style) {
          KlineIns.theme = style;
          switch_theme(style);
      },
  
      setLanguage: function (lang) {
          KlineIns.language = lang;
          chart_switch_language(lang);
      },
  
      setShowTrade: function (isShow) {
          this.showTrade = isShow;
          if (isShow) {
              $(".trade_container").show();
          } else {
              $(".trade_container").hide();
          }
          on_size(this.width, this.height);
      },
  
      toggleTrade: function () {
          if (!this.showTrade) {
              this.showTrade = true;
              $(".trade_container").show();
          } else {
              this.showTrade = false;
              $(".trade_container").hide();
          }
          on_size(this.width, this.height);
      },
  
      setIntervalTime: function (intervalTime) {
          this.intervalTime = intervalTime;
          if (this.debug) {
              // console.log('DEBUG: interval time changed to ' + intervalTime);
          }
      },
  
      connect: function () {
          if (this.type != 'socket') {
              if (this.debug) {
                  // console.log('DEBUG: this is for socket type');
              }
              return;
          }
          socketConnect();
          RequestData();
      },
  
      disconnect: function () {
          if (this.type != 'socket') {
              if (this.debug) {
                  // console.log('DEBUG: this is for socket type');
              }
              return;
          }
          if (this.socketClient) {
              this.socketClient.disconnect();
              KlineIns.socketConnected = false;
          }
          if (this.debug) {
              // console.log('DEBUG: socket disconnected');
          }
      },
  
  
      /*********************************************
       * Events
       *********************************************/
  
      onResize: function (width, height) {
          if (this.debug) {
              // console.log("DEBUG: chart resized to width: " + width + " height: " + height);
          }
      },
  
      onLangChange: function (lang) {
          if (this.debug) {
              // console.log("DEBUG: language changed to " + lang);
          }
      },
  
      onSymbolChange: function (symbol, symbolName) {
          if (this.debug) {
              console.log("DEBUG: symbol changed to " + symbol + " " + symbolName);
          }
      },
  
      onThemeChange: function (theme) {
          if (this.debug) {
              // console.log("DEBUG: theme changed to : " + theme);
          }
      },
  
      onRangeChange: function (range) {
          if (this.debug) {
              // console.log("DEBUG: range changed to " + range);
          }
      }
  
  };
  
  var KlineIns = null;
  
  
  var KlineTrade = function (option) {
  
      this.browerState = 0;
      this.tradeDate = new Date();
      this.tradesLimit = 20;
      this.lastDepth = null;
      this.depthShowSize = 15;
      this.priceDecimalDigits = 6;
      this.amountDecimalDigits = 4;
      this.symbol = null;
      this.curPrice = null;
      this.title = "";
  
      Object.assign(this, option);
  };
  
  KlineTrade.prototype = {
  
      reset: function (symbol) {
          this.symbol = symbol;
          this.lastDepth = null;
          this.curPrice = null;
          this.klineTradeInit = false;
          $("#trades .trades_list").empty();
          $("#gasks .table").empty();
          $("#gbids .table").empty();
          $("#asks .table").empty();
          $("#bids .table").empty();
      },
      pushTrades: function (array) {
          var $trades = $("#trades .trades_list");
          var totalUls = "";
          for (var i = 0; i < array.length; i++) {
              var item = array[i];
              if (i >= array.length - this.tradesLimit) {
                  this.tradeDate.setTime(item.time);
                  var dateStr = this.dateFormatTf(this.tradeDate.getHours())
                      + ":" + this.dateFormatTf(this.tradeDate.getMinutes())
                      + ":" + this.dateFormatTf(this.tradeDate.getSeconds());
                  var arr = (item.amount.toFixed(4) + "").split(".");
                  var price = item.price;
                  if (price > 1) {
                      price = price.toFixed(2)
                  }
                  if (price < 1 && price > 0.00001) {
                      price = price.toFixed(4)
                  }
                  if (price < 0.00001) {
                      price = price.toFixed(6)
                  }
  
                  if (this.klineTradeInit) {
                      totalUls = "<ul class='newul'><li class='tm'>" + dateStr + "</li><li class='pr-" + (item.type == 'buy' ? 'green' : 'red') + "'>" + price + "</li><li class='vl'>" + arr[0] + "<g>" + (arr.length > 1 ? '.' + arr[1] : '') + "</g></li></ul>" + totalUls;
                  } else {
                      totalUls = "<ul><li class='tm'>" + dateStr + "</li><li class='pr-" + (item.type == 'buy' ? 'green' : 'red') + "'>" + price + "</li><li class='vl'>" + arr[0] + "<g>" + (arr.length > 1 ? '.' + arr[1] : '') + "</g></li></ul>" + totalUls;
                  }
              }
          }
          var j = 0;
          var that = this;
          if (this.klineTradeInit) {
              clearInterval(myTime);
              var myTime = setInterval(function () {
                  var item = array[j]
                  //that.curPrice = item.price
                  var price = Number(item.price)
                  if (price > 1) {
                      price = price.toFixed(2)
                  }
  
                  if (price < 1 && price > 0.0001) {
                      price = price.toFixed(4)
                  }
                  if (price < 0.0001) {
                      price = price.toFixed(6)
                  }
                  that.curPrice = price;
                  $("div#price").attr("class", item.type == 'buy' ? 'green' : 'red').text(price);
                  j++;
                  if (j >= array.length) {
                      clearInterval(myTime);
                  }
              }, 100)
          } else {
              if (array.length > 0) {
                  //this.curPrice=array[array.length-1].price.toFixed(6);
                  var price = Number(array[array.length - 1].price)
                  if (price > 1) {
                      price = price.toFixed(2)
                  }
  
                  if (price < 1 && price > 0.0001) {
                      price = price.toFixed(4)
                  }
                  if (price < 0.0001) {
                      price = price.toFixed(6)
                  }
                  that.curPrice = price;
                  $("div#price").attr("class", array[array.length - 1].type == 'buy' ? 'green' : 'red').text(price);
              }
          }
  
          if (this.klineTradeInit) {
              $trades.prepend(totalUls);
          } else {
              $trades.append(totalUls);
          }
          totalUls = null;
          $trades.find("ul.newul").slideDown(1000, function () {
              $(this).removeClass("newul");
          });
          $trades.find("ul:gt(" + (this.tradesLimit - 1) + ")").remove();
      },
      updateDepth: function (data) {
          window._set_current_depth(data);
          if (!data) return;
          $("#gasks .table").html(this.getgview(this.getgasks(data.asks)));
          $("#gbids .table").html(this.getgview(this.getgbids(data.bids)));
          if (this.lastDepth == null) {
              this.lastDepth = {};
              this.lastDepth.asks = this.getAsks(data.asks, this.depthShowSize);
              this.depthInit(this.lastDepth.asks, $("#asks .table"));
              this.lastDepth.bids = this.getBids(data.bids, this.depthShowSize);
              this.depthInit(this.lastDepth.bids, $("#bids .table"));
          } else {
              var parentAsks = $("#asks .table");
              parentAsks.find("div.remove").remove();
              parentAsks.find("div.add").removeClass("add");
              var newasks = this.getAsks(data.asks, this.depthShowSize);
              var oldasks = this.lastDepth.asks;
              this.lastDepth.asks = newasks;
              this.asksAndBids(newasks.slice(0), oldasks, parentAsks);
  
              var parentBids = $("#bids .table");
              parentBids.find("div.remove").remove();
              parentBids.find("div.add").removeClass("add");
              var newbids = this.getBids(data.bids, this.depthShowSize);
              var oldbids = this.lastDepth.bids;
              this.lastDepth.bids = newbids;
              this.asksAndBids(newbids.slice(0), oldbids, $("#bids .table"));
          }
      },
      depthInit: function (data, $obj) {
          $obj.empty();
          if (data && data.length > 0) {
              var lastInt, view = "";
              for (var i = 0; i < data.length; i++) {
                  var arr = (data[i][0] + "").split(".");
                  var prices = this.getPrice(arr, lastInt);
                  lastInt = arr[0];
                  arr = (data[i][1] + "").split(".");
                  var amounts = this.getAmount(arr);
                  view += "<div class='row'><span class='price'>" + prices[0] + "<g>" + prices[1] + "</g></span> <span class='amount'>" + amounts[0] + "<g>" + amounts[1] + "</g></span></div>";
              }
              $obj.append(view);
              view = null;
          }
      },
      asksAndBids: function (addasks, oldasks, tbDiv) {
          for (var i = 0; i < oldasks.length; i++) {
              var isExist = false;
              for (var j = 0; j < addasks.length; j++) {
                  if (oldasks[i][0] == addasks[j][0]) {
                      isExist = true;
                      if (oldasks[i][1] != addasks[j][1]) {
                          var $amount = tbDiv.find("div:eq(" + i + ") .amount");
                          $amount.addClass(oldasks[i][1] > addasks[j][1] ? "red" : "green");
                          var amounts = this.getAmount((addasks[j][1] + "").split("."));
                          setTimeout((function ($amount, amounts) {
                              return function () {
                                  $amount.html(amounts[0] + "<g>" + amounts[1] + "</g>");
                                  $amount.removeClass("red").removeClass("green");
                                  $amount = null;
                                  amounts = null;
                              };
                          })($amount, amounts), 500);
                      }
                      addasks.splice(j, 1);
                      break;
                  }
              }
              if (!isExist) {
                  tbDiv.find("div:eq(" + i + ")").addClass("remove");
                  oldasks[i][2] = -1;//标识该数据对应div被移除
              }
          }
          for (var j = 0; j < oldasks.length; j++) {
              for (var i = 0; i < addasks.length; i++) {
                  if (addasks[i][0] > oldasks[j][0]) {
                      var arr = (addasks[i][1] + "").split(".");
                      var amounts = this.getAmount(arr);
                      tbDiv.find("div:eq(" + j + ")").before("<div class='row add'><span class='price'></span> <span class='amount'>" + amounts[0] + "<g>" + amounts[1] + "</g></span></div>");
                      oldasks.splice(j, 0, addasks[i]);
                      addasks.splice(i, 1);
                      break;
                  }
              }
          }
          var totalDiv = "";
          for (var i = 0; i < addasks.length; i++) {
              oldasks.push(addasks[i]);
              var arr = (addasks[i][1] + "").split(".");
              var amounts = this.getAmount(arr);
              totalDiv += "<div class='row add'><span class='price'></span> <span class='amount'>" + amounts[0] + "<g>" + amounts[1] + "</g></span></div>";
          }
          if (totalDiv.length > 0) {
              tbDiv.append(totalDiv);
          }
          totalDiv = null;
  
          var lastInt;
          for (var i = 0; i < oldasks.length; i++) {
              var $div = tbDiv.find("div:eq(" + i + ")");
              if (!(oldasks[i].length >= 3 && oldasks[i][2] == -1)) {
                  var arr = (oldasks[i][0] + "").split(".");
                  var prices = this.getPrice(arr, lastInt);
                  lastInt = arr[0];
                  $div.find(".price").html(prices[0] + "<g>" + prices[1] + "</g>");
              }
          }
          addasks = null;
          oldasks = null;
          tbDiv.find("div.add").slideDown(800);
          setTimeout((function ($remove, $add) {
              return function () {
                  $remove.slideUp(500, function () {
                      $(this).remove();
                  });
                  $add.removeClass("add");
              };
          })(tbDiv.find("div.remove"), tbDiv.find("div.add")), 1000);
  
      },
      getAsks: function (array, len) {
          if (array.length > len) {
              array.splice(0, array.length - len);
          }
          return array;
      },
      getBids: function (array, len) {
          if (array.length > len) {
              array.splice(len, array.length - 1);
          }
          return array;
      },
      getgview: function (g) {
          var gstr = "";
          var lastInt;
          for (var i = 0; i < g.length; i++) {
              var arr = g[i][0].split(".");
              if (arr.length == 1 || arr[0] != lastInt) {
                  gstr += "<div class='row'><span class='price'>" + g[i][0] + "</span> <span class='amount'>" + g[i][1] + "</span></div>";
                  lastInt = arr[0];
              } else {
                  gstr += "<div class='row'><span class='price'><h>" + arr[0] + ".</h>" + arr[1] + "</span> <span class='amount'>" + g[i][1] + "</span></div>";
              }
          }
          return gstr;
      },
      getgasks: function (array) {
          if (array.length < 2) {
              return [];
          }
          var low = array[array.length - 1][0];//最低价
          var high = array[0][0];//最高价
          var r = high - low;
          var block = this.getBlock(r, 100);
          var n = Math.abs(Number(Math.log(block) / Math.log(10))).toFixed(0);//精确小数位数
          if (r / block < 2) {
              block = block / 2;
              n++;
          }
          if (block >= 1) (n = 0);
          low = parseInt(low / block) * block;
          high = parseInt(high / block) * block;
          var gasks = [];
          var amount = 0;
          for (var i = array.length - 1; i >= 0; i--) {
              if (array[i][0] > low) {
                  var amountInt = parseInt(amount, 10);
                  if (amountInt > 0) {
                      gasks.unshift([Number(low).toFixed(n), amountInt]);
                  }
                  if (low >= high) {
                      break;
                  }
                  low += block;
              }
              amount += array[i][1];
          }
          return gasks;
      },
      getgbids: function (array) {
          if (array.length < 2) {
              return [];
          }
          var low = array[array.length - 1][0];
          var high = array[0][0];
          var r = high - low;
          var block = this.getBlock(r, 100);
          var n = Math.abs(Number(Math.log(block) / Math.log(10))).toFixed(0);//精确小数位数
          if (r / block < 2) {
              block = block / 2;
              n++;
          }
          if (block >= 1) (n = 0);
          low = parseInt(low / block) * block;
          high = parseInt(high / block) * block;
  
          var gbids = [];
          var amount = 0;
          for (var i = 0; i < array.length; i++) {
              if (array[i][0] < high) {
                  var amountInt = parseInt(amount, 10);
                  if (amountInt > 0) {
                      gbids.push([Number(high).toFixed(n), amountInt]);
                  }
                  if (high <= low) {
                      break;
                  }
                  high -= block;
              }
              amount += array[i][1];
          }
          return gbids;
      },
      getBlock: function (b, scale) {
          if (b > scale || b <= 0) {
              return scale;
          } else {
              scale = scale / 10;
              return this.getBlock(b, scale);
          }
      },
      getZeros: function (i) {
          var zeros = "";
          while (i > 0) {
              i--;
              zeros += "0";
          }
          return zeros;
      },
      getPrice: function (arr, lastInt) {
          var price1 = arr[0];
          if (lastInt == price1) {
              price1 = "<h>" + price1 + ".</h>";
          } else {
              price1 += ".";
          }
          var price2 = "";
          if (arr.length == 1) {
              price1 += "0";
              price2 = this.getZeros(this.priceDecimalDigits - 1);
          } else {
              price1 += arr[1];
              price2 = this.getZeros(this.priceDecimalDigits - arr[1].length);
          }
          return [price1, price2];
      },
      getAmount: function (arr) {
          var amount1 = arr[0];
          var amount2 = "";
          var zerosLen = this.amountDecimalDigits - amount1.length + 1;
          if (zerosLen > 0) {
              amount2 = ".";
              if (arr.length == 1) {
                  amount2 += this.getZeros(zerosLen);
              } else if (zerosLen > arr[1].length) {
                  amount2 += arr[1] + this.getZeros(zerosLen - arr[1].length);
              } else if (zerosLen == arr[1].length) {
                  amount2 += arr[1];
              } else {
                  amount2 += arr[1].substring(0, zerosLen);
              }
          }
          return [amount1, amount2];
      },
      dateFormatTf: function (i) {
          return (i < 10 ? '0' : '') + i;
      },
  };
  
  var KlineTradeIns = null;
  
  
  var classId = 0;
  
  /**
   * Class.
   */
  function create_class() {
      var argc = arguments.length;
      var func = function () {
      };
      var superClass;
      if (argc) {
          superClass = arguments[0];
          for (var k in superClass.prototype)
              func.prototype[k] = superClass.prototype[k];
      }
      for (var i = 1; i < argc; i++) {
          var feature = arguments[i];
          var f = feature.prototype.__construct;
          if (f) {
              if (!func.prototype.__featureConstructors)
                  func.prototype.__featureConstructors = [];
              func.prototype.__featureConstructors.push(f);
              delete feature.prototype.__construct;
          }
          for (var k in feature.prototype)
              func.prototype[k] = feature.prototype[k];
          if (f)
              feature.prototype.__construct = f;
      }
      var newClass = function () {
          if (this.__construct)
              this.__construct.apply(this, arguments);
          if (this.__featureConstructors) {
              var a = this.__featureConstructors;
              var i, c = a.length;
              for (i = 0; i < c; i++)
                  a[i].apply(this, arguments);
          }
      };
      func.prototype.__classId = classId++;
      if (superClass != undefined) {
          newClass.__super = superClass.prototype;
          func.prototype.__super = superClass;
      }
      newClass.prototype = new func();
      return newClass;
  }
  
  function is_instance(obj, clazz) {
      var classId = clazz.prototype.__classId;
      if (obj.__classId == classId)
          return true;
      var __super = obj.__super;
      while (__super != undefined) {
          if (__super.prototype.__classId == classId)
              return true;
          __super = __super.prototype.__super;
      }
      return false;
  }
  
  /**
   * Class: MEvent.
   */
  var MEvent = create_class();
  MEvent.prototype.__construct = function () {
      this._handlers = [];
  };
  MEvent.prototype.addHandler = function (o, f) {
      if (this._indexOf(o, f) < 0)
          this._handlers.push({obj: o, func: f});
  };
  MEvent.prototype.removeHandler = function (o, f) {
      var i = this._indexOf(o, f);
      if (i >= 0)
          this._handlers.splice(i, 1);
  };
  MEvent.prototype.raise = function (s, g) {
      var a = this._handlers;
      var e, i, c = a.length;
      for (i = 0; i < c; i++) {
          e = a[i];
          e.func.call(e.obj, s, g);
      }
  };
  MEvent.prototype._indexOf = function (o, f) {
      var a = this._handlers;
      var e, i, c = a.length;
      for (i = 0; i < c; i++) {
          e = a[i];
          if (o == e.obj && f == e.func)
              return i;
      }
      return -1;
  };
  String.fromFloat = function (v, fractionDigits) {
      // debugger;
      var text = v.toFixed(fractionDigits);
      for (var i = text.length - 1; i >= 0; i--) {
          if (text[i] == '.')
              return text.substring(0, i);
          if (text[i] != '0')
              return text.substring(0, i + 1);
      }
  };
  var ExprEnv = create_class();
  ExprEnv.get = function () {
      return ExprEnv.inst;
  };
  ExprEnv.set = function (env) {
      ExprEnv.inst = env;
  };
  ExprEnv.prototype.getDataSource = function () {
      return this._ds;
  };
  ExprEnv.prototype.setDataSource = function (ds) {
      return this._ds = ds;
  };
  ExprEnv.prototype.getFirstIndex = function () {
      return this._firstIndex;
  };
  ExprEnv.prototype.setFirstIndex = function (n) {
      return this._firstIndex = n;
  };
  var Expr = create_class();
  Expr.prototype.__construct = function () {
      this._rid = 0;
  };
  Expr.prototype.execute = function (index) {
  };
  Expr.prototype.reserve = function (rid, count) {
  };
  Expr.prototype.clear = function () {
  };
  var OpenExpr = create_class(Expr);
  var HighExpr = create_class(Expr);
  var LowExpr = create_class(Expr);
  var CloseExpr = create_class(Expr);
  var VolumeExpr = create_class(Expr);
  OpenExpr.prototype.execute = function (index) {
      return ExprEnv.get()._ds.getDataAt(index).open;
  };
  HighExpr.prototype.execute = function (index) {
      return ExprEnv.get()._ds.getDataAt(index).high;
  };
  LowExpr.prototype.execute = function (index) {
      return ExprEnv.get()._ds.getDataAt(index).low;
  };
  CloseExpr.prototype.execute = function (index) {
      return ExprEnv.get()._ds.getDataAt(index).close;
  };
  VolumeExpr.prototype.execute = function (index) {
      return ExprEnv.get()._ds.getDataAt(index).volume;
  };
  var ConstExpr = create_class(Expr);
  ConstExpr.prototype.__construct = function (v) {
      ConstExpr.__super.__construct.call(this);
      this._value = v;
  };
  ConstExpr.prototype.execute = function (index) {
      return this._value;
  };
  var ParameterExpr = create_class(Expr);
  ParameterExpr.prototype.__construct = function (name, minValue, maxValue, defaultValue) {
      ParameterExpr.__super.__construct.call(this);
      this._name = name;
      this._minValue = minValue;
      this._maxValue = maxValue;
      this._value = this._defaultValue = defaultValue;
  };
  ParameterExpr.prototype.execute = function (index) {
      return this._value;
  };
  ParameterExpr.prototype.getMinValue = function () {
      return this._minValue;
  };
  ParameterExpr.prototype.getMaxValue = function () {
      return this._maxValue;
  };
  ParameterExpr.prototype.getDefaultValue = function () {
      return this._defaultValue;
  };
  ParameterExpr.prototype.getValue = function () {
      return this._value;
  };
  ParameterExpr.prototype.setValue = function (v) {
      if (v == 0)
          this._value = 0;
      else if (v < this._minValue)
          this._value = this._minValue;
      else if (v > this._maxValue)
          this._value = this._maxValue;
      else
          this._value = v;
  };
  var OpAExpr = create_class(Expr);
  var OpABExpr = create_class(Expr);
  var OpABCExpr = create_class(Expr);
  var OpABCDExpr = create_class(Expr);
  OpAExpr.prototype.__construct = function (a) {
      OpAExpr.__super.__construct.call(this);
      this._exprA = a;
  };
  OpAExpr.prototype.reserve = function (rid, count) {
      if (this._rid < rid) {
          this._rid = rid;
          this._exprA.reserve(rid, count);
      }
  };
  OpAExpr.prototype.clear = function () {
      this._exprA.clear();
  };
  OpABExpr.prototype.__construct = function (a, b) {
      OpABExpr.__super.__construct.call(this);
      this._exprA = a;
      this._exprB = b;
  };
  OpABExpr.prototype.reserve = function (rid, count) {
      if (this._rid < rid) {
          this._rid = rid;
          this._exprA.reserve(rid, count);
          this._exprB.reserve(rid, count);
      }
  };
  OpABExpr.prototype.clear = function () {
      this._exprA.clear();
      this._exprB.clear();
  };
  OpABCExpr.prototype.__construct = function (a, b, c) {
      OpABCExpr.__super.__construct.call(this);
      this._exprA = a;
      this._exprB = b;
      this._exprC = c;
  };
  OpABCExpr.prototype.reserve = function (rid, count) {
      if (this._rid < rid) {
          this._rid = rid;
          this._exprA.reserve(rid, count);
          this._exprB.reserve(rid, count);
          this._exprC.reserve(rid, count);
      }
  };
  OpABCExpr.prototype.clear = function () {
      this._exprA.clear();
      this._exprB.clear();
      this._exprC.clear();
  };
  OpABCDExpr.prototype.__construct = function (a, b, c, d) {
      OpABCDExpr.__super.__construct.call(this);
      this._exprA = a;
      this._exprB = b;
      this._exprC = c;
      this._exprD = d;
  };
  OpABCDExpr.prototype.reserve = function (rid, count) {
      if (this._rid < rid) {
          this._rid = rid;
          this._exprA.reserve(rid, count);
          this._exprB.reserve(rid, count);
          this._exprC.reserve(rid, count);
          this._exprD.reserve(rid, count);
      }
  };
  OpABCDExpr.prototype.clear = function () {
      this._exprA.clear();
      this._exprB.clear();
      this._exprC.clear();
      this._exprD.clear();
  };
  var NegExpr = create_class(OpAExpr);
  NegExpr.prototype.__construct = function (a) {
      NegExpr.__super.__construct.call(this, a);
  };
  NegExpr.prototype.execute = function (index) {
      return -(this._exprA.execute(index));
  };
  var AddExpr = create_class(OpABExpr);
  var SubExpr = create_class(OpABExpr);
  var MulExpr = create_class(OpABExpr);
  var DivExpr = create_class(OpABExpr);
  AddExpr.prototype.__construct = function (a, b) {
      AddExpr.__super.__construct.call(this, a, b);
  };
  SubExpr.prototype.__construct = function (a, b) {
      SubExpr.__super.__construct.call(this, a, b);
  };
  MulExpr.prototype.__construct = function (a, b) {
      MulExpr.__super.__construct.call(this, a, b);
  };
  DivExpr.prototype.__construct = function (a, b) {
      DivExpr.__super.__construct.call(this, a, b);
  };
  AddExpr.prototype.execute = function (index) {
      return this._exprA.execute(index) + this._exprB.execute(index);
  };
  SubExpr.prototype.execute = function (index) {
      return this._exprA.execute(index) - this._exprB.execute(index);
  };
  MulExpr.prototype.execute = function (index) {
      return this._exprA.execute(index) * this._exprB.execute(index);
  };
  DivExpr.prototype.execute = function (index) {
      var a = this._exprA.execute(index);
      var b = this._exprB.execute(index);
      if (a == 0)
          return a;
      if (b == 0)
          return (a > 0) ? 1000000 : -1000000;
      return a / b;
  };
  var GtExpr = create_class(OpABExpr);
  var GeExpr = create_class(OpABExpr);
  var LtExpr = create_class(OpABExpr);
  var LeExpr = create_class(OpABExpr);
  var EqExpr = create_class(OpABExpr);
  GtExpr.prototype.__construct = function (a, b) {
      GtExpr.__super.__construct.call(this, a, b);
  };
  GeExpr.prototype.__construct = function (a, b) {
      GeExpr.__super.__construct.call(this, a, b);
  };
  LtExpr.prototype.__construct = function (a, b) {
      LtExpr.__super.__construct.call(this, a, b);
  };
  LeExpr.prototype.__construct = function (a, b) {
      LeExpr.__super.__construct.call(this, a, b);
  };
  EqExpr.prototype.__construct = function (a, b) {
      EqExpr.__super.__construct.call(this, a, b);
  };
  GtExpr.prototype.execute = function (index) {
      return this._exprA.execute(index) > this._exprB.execute(index) ? 1 : 0;
  };
  GeExpr.prototype.execute = function (index) {
      return this._exprA.execute(index) >= this._exprB.execute(index) ? 1 : 0;
  };
  LtExpr.prototype.execute = function (index) {
      return this._exprA.execute(index) < this._exprB.execute(index) ? 1 : 0;
  };
  LeExpr.prototype.execute = function (index) {
      return this._exprA.execute(index) <= this._exprB.execute(index) ? 1 : 0;
  };
  EqExpr.prototype.execute = function (index) {
      return this._exprA.execute(index) == this._exprB.execute(index) ? 1 : 0;
  };
  var MaxExpr = create_class(OpABExpr);
  MaxExpr.prototype.__construct = function (a, b) {
      MaxExpr.__super.__construct.call(this, a, b);
  };
  MaxExpr.prototype.execute = function (index) {
      return Math.max(this._exprA.execute(index), this._exprB.execute(index));
  };
  var AbsExpr = create_class(OpAExpr);
  AbsExpr.prototype.__construct = function (a) {
      AbsExpr.__super.__construct.call(this, a);
  };
  AbsExpr.prototype.execute = function (index) {
      return Math.abs(this._exprA.execute(index));
  };
  var RefExpr = create_class(OpABExpr);
  RefExpr.prototype.__construct = function (a, b) {
      RefExpr.__super.__construct.call(this, a, b);
      this._offset = -1;
  };
  RefExpr.prototype.execute = function (index) {
      if (this._offset < 0) {
          this._offset = this._exprB.execute(index);
          if (this._offset < 0)
              throw "offset < 0";
      }
      index -= this._offset;
      if (index < 0)
          throw "index < 0";
      var result = this._exprA.execute(index);
      if (isNaN(result))
          throw "NaN";
      return result;
  };
  var AndExpr = create_class(OpABExpr);
  var OrExpr = create_class(OpABExpr);
  AndExpr.prototype.__construct = function (a, b) {
      AndExpr.__super.__construct.call(this, a, b);
  };
  OrExpr.prototype.__construct = function (a, b) {
      OrExpr.__super.__construct.call(this, a, b);
  };
  AndExpr.prototype.execute = function (index) {
      return (this._exprA.execute(index) != 0) && (this._exprB.execute(index) != 0) ? 1 : 0;
  };
  OrExpr.prototype.execute = function (index) {
      return (this._exprA.execute(index) != 0) || (this._exprB.execute(index) != 0) ? 1 : 0;
  };
  var IfExpr = create_class(OpABCExpr);
  IfExpr.prototype.__construct = function (a, b, c) {
      IfExpr.__super.__construct.call(this, a, b, c);
  };
  IfExpr.prototype.execute = function (index) {
      return this._exprA.execute(index) != 0 ? this._exprB.execute(index) : this._exprC.execute(index);
  };
  var AssignExpr = create_class(OpAExpr);
  AssignExpr.prototype.__construct = function (name, a) {
      AssignExpr.__super.__construct.call(this, a);
      this._name = name;
      this._buf = [];
  };
  AssignExpr.prototype.getName = function () {
      return this._name;
  };
  AssignExpr.prototype.execute = function (index) {
      return this._buf[index];
  };
  AssignExpr.prototype.assign = function (index) {
      this._buf[index] = this._exprA.execute(index);
      if (ExprEnv.get()._firstIndex >= 0)
          if (isNaN(this._buf[index]) && !isNaN(this._buf[index - 1]))
              throw this._name + ".assign(" + index + "): NaN";
  };
  AssignExpr.prototype.reserve = function (rid, count) {
      if (this._rid < rid) {
          for (var c = count; c > 0; c--)
              this._buf.push(NaN);
      }
      AssignExpr.__super.reserve.call(this, rid, count);
  };
  AssignExpr.prototype.clear = function () {
      AssignExpr.__super.clear.call(this);
      this._buf = [];
  };
  var OutputStyle = {
      None: 0,
      Line: 1,
      VolumeStick: 2,
      MACDStick: 3,
      SARPoint: 4
  };
  var OutputExpr = create_class(AssignExpr);
  OutputExpr.prototype.__construct = function (name, a, style, color) {
      OutputExpr.__super.__construct.call(this, name, a);
      this._style = (style === undefined) ? OutputStyle.Line : style;
      this._color = color;
  };
  OutputExpr.prototype.getStyle = function () {
      return this._style;
  };
  OutputExpr.prototype.getColor = function () {
      return this._color;
  };
  var RangeOutputExpr = create_class(OutputExpr);
  RangeOutputExpr.prototype.__construct = function (name, a, style, color) {
      RangeOutputExpr.__super.__construct.call(this, name, a, style, color);
  };
  RangeOutputExpr.prototype.getName = function () {
      return this._name + this._exprA.getRange();
  };
  var RangeExpr = create_class(OpABExpr);
  RangeExpr.prototype.__construct = function (a, b) {
      RangeExpr.__super.__construct.call(this, a, b);
      this._range = -1;
      this._buf = [];
  };
  RangeExpr.prototype.getRange = function () {
      return this._range;
  };
  RangeExpr.prototype.initRange = function () {
      this._range = this._exprB.execute(0);
  };
  RangeExpr.prototype.execute = function (index) {
      if (this._range < 0)
          this.initRange();
      var rA = this._buf[index].resultA = this._exprA.execute(index);
      var r = this._buf[index].result = this.calcResult(index, rA);
      return r;
  };
  RangeExpr.prototype.reserve = function (rid, count) {
      if (this._rid < rid) {
          for (var c = count; c > 0; c--)
              this._buf.push({resultA: NaN, result: NaN});
      }
      RangeExpr.__super.reserve.call(this, rid, count);
  };
  RangeExpr.prototype.clear = function () {
      RangeExpr.__super.clear.call(this);
      this._range = -1;
      this._buf = [];
  };
  var HhvExpr = create_class(RangeExpr);
  var LlvExpr = create_class(RangeExpr);
  HhvExpr.prototype.__construct = function (a, b) {
      HhvExpr.__super.__construct.call(this, a, b);
  };
  LlvExpr.prototype.__construct = function (a, b) {
      LlvExpr.__super.__construct.call(this, a, b);
  };
  HhvExpr.prototype.calcResult = function (index, resultA) {
      if (this._range == 0)
          return NaN;
      var first = ExprEnv.get()._firstIndex;
      if (first < 0)
          return resultA;
      if (index > first) {
          var n = this._range;
          var result = resultA;
          var start = index - n + 1;
          var i = Math.max(first, start);
          for (; i < index; i++) {
              var p = this._buf[i];
              if (result < p.resultA)
                  result = p.resultA;
          }
          return result;
      } else {
          return resultA;
      }
  };
  LlvExpr.prototype.calcResult = function (index, resultA) {
      if (this._range == 0)
          return NaN;
      var first = ExprEnv.get()._firstIndex;
      if (first < 0)
          return resultA;
      if (index > first) {
          var n = this._range;
          var result = resultA;
          var start = index - n + 1;
          var i = Math.max(first, start);
          for (; i < index; i++) {
              var p = this._buf[i];
              if (result > p.resultA)
                  result = p.resultA;
          }
          return result;
      } else {
          return resultA;
      }
  };
  var CountExpr = create_class(RangeExpr);
  CountExpr.prototype.__construct = function (a, b) {
      CountExpr.__super.__construct.call(this, a, b);
  };
  CountExpr.prototype.calcResult = function (index, resultA) {
      if (this._range == 0)
          return NaN;
      var first = ExprEnv.get()._firstIndex;
      if (first < 0)
          return 0;
      if (index >= first) {
          var n = this._range - 1;
          if (n > index - first)
              n = index - first;
          var count = 0;
          for (; n >= 0; n--) {
              if (this._buf[index - n].resultA != 0.0)
                  count++;
          }
          return count;
      } else {
          return 0;
      }
  };
  var SumExpr = create_class(RangeExpr);
  SumExpr.prototype.__construct = function (a, b) {
      SumExpr.__super.__construct.call(this, a, b);
  };
  SumExpr.prototype.calcResult = function (index, resultA) {
      var first = ExprEnv.get()._firstIndex;
      if (first < 0)
          return resultA;
      if (index > first) {
          var n = this._range;
          if (n == 0 || n >= index + 1 - first) {
              return this._buf[index - 1].result + resultA;
          }
          return this._buf[index - 1].result + resultA - this._buf[index - n].resultA;
      } else {
          return resultA;
      }
  };
  var StdExpr = create_class(RangeExpr);
  StdExpr.prototype.__construct = function (a, b) {
      StdExpr.__super.__construct.call(this, a, b);
  };
  StdExpr.prototype.calcResult = function (index, resultA) {
      if (this._range == 0)
          return NaN;
      var stdData = this._stdBuf[index];
      var first = ExprEnv.get()._firstIndex;
      if (first < 0) {
          stdData.resultMA = resultA;
          return 0.0;
      }
      if (index > first) {
          var n = this._range;
          if (n >= index + 1 - first) {
              n = index + 1 - first;
              stdData.resultMA = this._stdBuf[index - 1].resultMA * (1.0 - 1.0 / n) + (resultA / n);
          } else {
              stdData.resultMA = this._stdBuf[index - 1].resultMA + (resultA - this._buf[index - n].resultA) / n;
          }
          var sum = 0;
          for (var i = index - n + 1; i <= index; i++)
              sum += Math.pow(this._buf[i].resultA - stdData.resultMA, 2);
          return Math.sqrt(sum / n);
      }
      stdData.resultMA = resultA;
      return 0.0;
  };
  StdExpr.prototype.reserve = function (rid, count) {
      if (this._rid < rid) {
          for (var c = count; c > 0; c--)
              this._stdBuf.push({resultMA: NaN});
      }
      StdExpr.__super.reserve.call(this, rid, count);
  };
  StdExpr.prototype.clear = function () {
      StdExpr.__super.clear.call(this);
      this._stdBuf = [];
  };
  var MaExpr = create_class(RangeExpr);
  MaExpr.prototype.__construct = function (a, b) {
      MaExpr.__super.__construct.call(this, a, b);
  };
  MaExpr.prototype.calcResult = function (index, resultA) {
      if (this._range == 0)
          return NaN;
      var first = ExprEnv.get()._firstIndex;
      if (first < 0)
          return resultA;
      if (index > first) {
          var n = this._range;
          if (n >= index + 1 - first) {
              n = index + 1 - first;
              return this._buf[index - 1].result * (1.0 - 1.0 / n) + (resultA / n);
          }
          return this._buf[index - 1].result + (resultA - this._buf[index - n].resultA) / n;
      } else {
          return resultA;
      }
  };
  var EmaExpr = create_class(RangeExpr);
  EmaExpr.prototype.__construct = function (a, b) {
      EmaExpr.__super.__construct.call(this, a, b);
  };
  EmaExpr.prototype.initRange = function () {
      EmaExpr.__super.initRange.call(this);
      this._alpha = 2.0 / (this._range + 1);
  };
  EmaExpr.prototype.calcResult = function (index, resultA) {
      if (this._range == 0)
          return NaN;
      var first = ExprEnv.get()._firstIndex;
      if (first < 0)
          return resultA;
      if (index > first) {
          var prev = this._buf[index - 1];
          return this._alpha * (resultA - prev.result) + prev.result;
      }
      return resultA;
  };
  var ExpmemaExpr = create_class(EmaExpr);
  ExpmemaExpr.prototype.__construct = function (a, b) {
      ExpmemaExpr.__super.__construct.call(this, a, b);
  };
  ExpmemaExpr.prototype.calcResult = function (index, resultA) {
      var first = ExprEnv.get()._firstIndex;
      if (first < 0)
          return resultA;
      if (index > first) {
          var n = this._range;
          var prev = this._buf[index - 1];
          if (n >= index + 1 - first) {
              n = index + 1 - first;
              return prev.result * (1.0 - 1.0 / n) + (resultA / n);
          }
          return this._alpha * (resultA - prev.result) + prev.result;
      }
      return resultA;
  };
  var SmaExpr = create_class(RangeExpr);
  SmaExpr.prototype.__construct = function (a, b, c) {
      SmaExpr.__super.__construct.call(this, a, b);
      this._exprC = c;
      this._mul;
  };
  SmaExpr.prototype.initRange = function () {
      SmaExpr.__super.initRange.call(this);
      this._mul = this._exprC.execute(0);
  };
  SmaExpr.prototype.calcResult = function (index, resultA) {
      if (this._range == 0)
          return NaN;
      var first = ExprEnv.get()._firstIndex;
      if (first < 0)
          return resultA;
      if (index > first) {
          var n = this._range;
          if (n > index + 1 - first)
              n = index + 1 - first;
          return ((n - 1) * this._buf[index - 1].result + resultA * this._mul) / n;
      }
      return resultA;
  };
  var SarExpr = create_class(OpABCDExpr);
  SarExpr.prototype.__construct = function (a, b, c, d) {
      SarExpr.__super.__construct.call(this, a, b, c, d);
      this._buf = [];
      this._range = -1;
      this._min;
      this._step;
      this._max;
  };
  SarExpr.prototype.execute = function (index) {
      if (this._range < 0) {
          this._range = this._exprA.execute(0);
          this._min = this._exprB.execute(0) / 100.0;
          this._step = this._exprC.execute(0) / 100.0;
          this._max = this._exprD.execute(0) / 100.0;
      }
      var data = this._buf[index];
      var exprEnv = ExprEnv.get();
      var first = exprEnv._firstIndex;
      if (first < 0) {
          data.longPos = true;
          data.sar = exprEnv._ds.getDataAt(index).low;
          data.ep = exprEnv._ds.getDataAt(index).high;
          data.af = 0.02;
      } else {
          var high = exprEnv._ds.getDataAt(index).high;
          var low = exprEnv._ds.getDataAt(index).low;
          var prev = this._buf[index - 1];
          data.sar = prev.sar + prev.af * (prev.ep - prev.sar);
          if (prev.longPos) {
              data.longPos = true;
              if (high > prev.ep) {
                  data.ep = high;
                  data.af = Math.min(prev.af + this._step, this._max);
              } else {
                  data.ep = prev.ep;
                  data.af = prev.af;
              }
              if (data.sar > low) {
                  data.longPos = false;
                  var i = index - this._range + 1;
                  for (i = Math.max(i, first); i < index; i++) {
                      var h = exprEnv._ds.getDataAt(i).high;
                      if (high < h) high = h;
                  }
                  data.sar = high;
                  data.ep = low;
                  data.af = 0.02;
              }
          } else {
              data.longPos = false;
              if (low < prev.ep) {
                  data.ep = low;
                  data.af = Math.min(prev.af + this._step, this._max);
              } else {
                  data.ep = prev.ep;
                  data.af = prev.af;
              }
              if (data.sar < high) {
                  data.longPos = true;
                  var i = index - this._range + 1;
                  for (i = Math.max(i, first); i < index; i++) {
                      var l = exprEnv._ds.getDataAt(i).low;
                      if (low > l) low = l;
                  }
                  data.sar = low;
                  data.ep = high;
                  data.af = 0.02;
              }
          }
      }
      return data.sar;
  };
  SarExpr.prototype.reserve = function (rid, count) {
      if (this._rid < rid) {
          for (var c = count; c > 0; c--)
              this._buf.push({longPos: true, sar: NaN, ep: NaN, af: NaN});
      }
      SarExpr.__super.reserve.call(this, rid, count);
  };
  SarExpr.prototype.clear = function () {
      SarExpr.__super.clear.call(this);
      this._range = -1;
  };
  var Indicator = create_class();
  Indicator.prototype.__construct = function () {
      this._exprEnv = new ExprEnv();
      this._rid = 0;
      this._params = [];
      this._assigns = [];
      this._outputs = [];
  };
  Indicator.prototype.addParameter = function (expr) {
      this._params.push(expr);
  };
  Indicator.prototype.addAssign = function (expr) {
      this._assigns.push(expr);
  };
  Indicator.prototype.addOutput = function (expr) {
      this._outputs.push(expr);
  };
  Indicator.prototype.getParameterCount = function () {
      return this._params.length;
  };
  Indicator.prototype.getParameterAt = function (index) {
      return this._params[index];
  };
  Indicator.prototype.getOutputCount = function () {
      return this._outputs.length;
  };
  Indicator.prototype.getOutputAt = function (index) {
      return this._outputs[index];
  };
  Indicator.prototype.clear = function () {
      this._exprEnv.setFirstIndex(-1);
      var i, cnt;
      cnt = this._assigns.length;
      for (i = 0; i < cnt; i++) {
          this._assigns[i].clear();
      }
      cnt = this._outputs.length;
      for (i = 0; i < cnt; i++) {
          this._outputs[i].clear();
      }
  };
  Indicator.prototype.reserve = function (count) {
      this._rid++;
      var i, cnt;
      cnt = this._assigns.length;
      for (i = 0; i < cnt; i++) {
          this._assigns[i].reserve(this._rid, count);
      }
      cnt = this._outputs.length;
      for (i = 0; i < cnt; i++) {
          this._outputs[i].reserve(this._rid, count);
      }
  };
  Indicator.prototype.execute = function (ds, index) {
      if (index < 0)
          return;
      this._exprEnv.setDataSource(ds);
      ExprEnv.set(this._exprEnv);
      try {
          var i, cnt;
          cnt = this._assigns.length;
          for (i = 0; i < cnt; i++) {
              this._assigns[i].assign(index);
          }
          cnt = this._outputs.length;
          for (i = 0; i < cnt; i++) {
              this._outputs[i].assign(index);
          }
          if (this._exprEnv.getFirstIndex() < 0)
              this._exprEnv.setFirstIndex(index);
      } catch (e) {
          if (this._exprEnv.getFirstIndex() >= 0) {
              alert(e);
              throw e;
          }
      }
  };
  Indicator.prototype.getParameters = function () {
      var params = [];
      var i, cnt = this._params.length;
      for (i = 0; i < cnt; i++)
          params.push(this._params[i].getValue());
      return params;
  };
  Indicator.prototype.setParameters = function (params) {
      if ((params instanceof Array) && params.length == this._params.length) {
          for (var i in this._params)
              this._params[i].setValue(params[i]);
      }
  };
  var HLCIndicator = create_class(Indicator);
  HLCIndicator.prototype.__construct = function () {
      HLCIndicator.__super.__construct.call(this);
      var M1 = new ParameterExpr("M1", 2, 1000, 60);
      this.addParameter(M1);
      this.addOutput(new OutputExpr("HIGH",
          new HighExpr(),
          OutputStyle.None
      ));
      this.addOutput(new OutputExpr("LOW",
          new LowExpr(),
          OutputStyle.None
      ));
      this.addOutput(new OutputExpr("CLOSE",
          new CloseExpr(),
          OutputStyle.Line,
          Theme.Color.Indicator0
      ));
      this.addOutput(new RangeOutputExpr("MA",
          new MaExpr(new CloseExpr(), M1),
          OutputStyle.Line,
          Theme.Color.Indicator1
      ));
  };
  HLCIndicator.prototype.getName = function () {
      return "CLOSE";
  };
  var MAIndicator = create_class(Indicator);
  MAIndicator.prototype.__construct = function () {
      MAIndicator.__super.__construct.call(this);
      var M1 = new ParameterExpr("M1", 2, 1000, 7);
      var M2 = new ParameterExpr("M2", 2, 1000, 30);
      var M3 = new ParameterExpr("M3", 2, 1000, 0);
      var M4 = new ParameterExpr("M4", 2, 1000, 0);
      var M5 = new ParameterExpr("M5", 2, 1000, 0);
      var M6 = new ParameterExpr("M6", 2, 1000, 0);
      this.addParameter(M1);
      this.addParameter(M2);
      this.addParameter(M3);
      this.addParameter(M4);
      this.addParameter(M5);
      this.addParameter(M6);
      this.addOutput(new RangeOutputExpr("MA",
          new MaExpr(new CloseExpr(), M1)
      ));
      this.addOutput(new RangeOutputExpr("MA",
          new MaExpr(new CloseExpr(), M2)
      ));
      this.addOutput(new RangeOutputExpr("MA",
          new MaExpr(new CloseExpr(), M3)
      ));
      this.addOutput(new RangeOutputExpr("MA",
          new MaExpr(new CloseExpr(), M4)
      ));
      this.addOutput(new RangeOutputExpr("MA",
          new MaExpr(new CloseExpr(), M5)
      ));
      this.addOutput(new RangeOutputExpr("MA",
          new MaExpr(new CloseExpr(), M6)
      ));
  };
  MAIndicator.prototype.getName = function () {
      return "MA";
  };
  var EMAIndicator = create_class(Indicator);
  EMAIndicator.prototype.__construct = function () {
      EMAIndicator.__super.__construct.call(this);
      var M1 = new ParameterExpr("M1", 2, 1000, 7);
      var M2 = new ParameterExpr("M2", 2, 1000, 30);
      var M3 = new ParameterExpr("M3", 2, 1000, 0);
      var M4 = new ParameterExpr("M4", 2, 1000, 0);
      var M5 = new ParameterExpr("M5", 2, 1000, 0);
      var M6 = new ParameterExpr("M6", 2, 1000, 0);
      this.addParameter(M1);
      this.addParameter(M2);
      this.addParameter(M3);
      this.addParameter(M4);
      this.addParameter(M5);
      this.addParameter(M6);
      this.addOutput(new RangeOutputExpr("EMA",
          new EmaExpr(new CloseExpr(), M1)
      ));
      this.addOutput(new RangeOutputExpr("EMA",
          new EmaExpr(new CloseExpr(), M2)
      ));
      this.addOutput(new RangeOutputExpr("EMA",
          new EmaExpr(new CloseExpr(), M3)
      ));
      this.addOutput(new RangeOutputExpr("EMA",
          new EmaExpr(new CloseExpr(), M4)
      ));
      this.addOutput(new RangeOutputExpr("EMA",
          new EmaExpr(new CloseExpr(), M5)
      ));
      this.addOutput(new RangeOutputExpr("EMA",
          new EmaExpr(new CloseExpr(), M6)
      ));
  };
  EMAIndicator.prototype.getName = function () {
      return "EMA";
  };
  var VOLUMEIndicator = create_class(Indicator);
  VOLUMEIndicator.prototype.__construct = function () {
      VOLUMEIndicator.__super.__construct.call(this);
      var M1 = new ParameterExpr("M1", 2, 500, 5);
      var M2 = new ParameterExpr("M2", 2, 500, 10);
      this.addParameter(M1);
      this.addParameter(M2);
      var VOLUME = new OutputExpr("VOLUME",
          new VolumeExpr(),
          OutputStyle.VolumeStick,
          Theme.Color.Text4
      );
      this.addOutput(VOLUME);
      this.addOutput(new RangeOutputExpr("MA",
          new MaExpr(VOLUME, M1),
          OutputStyle.Line,
          Theme.Color.Indicator0
      ));
      this.addOutput(new RangeOutputExpr("MA",
          new MaExpr(VOLUME, M2),
          OutputStyle.Line,
          Theme.Color.Indicator1
      ));
  };
  VOLUMEIndicator.prototype.getName = function () {
      return "VOLUME";
  };
  var MACDIndicator = create_class(Indicator);
  MACDIndicator.prototype.__construct = function () {
      MACDIndicator.__super.__construct.call(this);
      var SHORT = new ParameterExpr("SHORT", 2, 200, 12);
      var LONG = new ParameterExpr("LONG", 2, 200, 26);
      var MID = new ParameterExpr("MID", 2, 200, 9);
      this.addParameter(SHORT);
      this.addParameter(LONG);
      this.addParameter(MID);
      var DIF = new OutputExpr("DIF",
          new SubExpr(
              new EmaExpr(new CloseExpr(), SHORT),
              new EmaExpr(new CloseExpr(), LONG)
          )
      );
      this.addOutput(DIF);
      var DEA = new OutputExpr("DEA",
          new EmaExpr(DIF, MID)
      );
      this.addOutput(DEA);
      var MACD = new OutputExpr("MACD",
          new MulExpr(
              new SubExpr(DIF, DEA),
              new ConstExpr(2)
          ),
          OutputStyle.MACDStick
      );
      this.addOutput(MACD);
  };
  MACDIndicator.prototype.getName = function () {
      return "MACD";
  };
  var DMIIndicator = create_class(Indicator);
  DMIIndicator.prototype.__construct = function () {
      DMIIndicator.__super.__construct.call(this);
      var N = new ParameterExpr("N", 2, 90, 14);
      var MM = new ParameterExpr("MM", 2, 60, 6);
      this.addParameter(N);
      this.addParameter(MM);
      var MTR = new AssignExpr("MTR",
          new ExpmemaExpr(
              new MaxExpr(
                  new MaxExpr(
                      new SubExpr(new HighExpr(), new LowExpr()),
                      new AbsExpr(
                          new SubExpr(
                              new HighExpr(),
                              new RefExpr(new CloseExpr(), new ConstExpr(1))
                          )
                      )
                  ),
                  new AbsExpr(
                      new SubExpr(
                          new RefExpr(new CloseExpr(), new ConstExpr(1)),
                          new LowExpr()
                      )
                  )
              ),
              N
          )
      );
      this.addAssign(MTR);
      var HD = new AssignExpr("HD",
          new SubExpr(
              new HighExpr(),
              new RefExpr(new HighExpr(), new ConstExpr(1))
          )
      );
      this.addAssign(HD);
      var LD = new AssignExpr("LD",
          new SubExpr(
              new RefExpr(new LowExpr(), new ConstExpr(1)),
              new LowExpr()
          )
      );
      this.addAssign(LD);
      var DMP = new AssignExpr("DMP",
          new ExpmemaExpr(
              new IfExpr(
                  new AndExpr(
                      new GtExpr(HD, new ConstExpr(0)),
                      new GtExpr(HD, LD)
                  ),
                  HD,
                  new ConstExpr(0)
              ),
              N
          )
      );
      this.addAssign(DMP);
      var DMM = new AssignExpr("DMM",
          new ExpmemaExpr(
              new IfExpr(
                  new AndExpr(
                      new GtExpr(LD, new ConstExpr(0)),
                      new GtExpr(LD, HD)
                  ),
                  LD,
                  new ConstExpr(0)
              ),
              N
          )
      );
      this.addAssign(DMM);
      var PDI = new OutputExpr("PDI",
          new MulExpr(
              new DivExpr(DMP, MTR),
              new ConstExpr(100)
          )
      );
      this.addOutput(PDI);
      var MDI = new OutputExpr("MDI",
          new MulExpr(
              new DivExpr(DMM, MTR),
              new ConstExpr(100)
          )
      );
      this.addOutput(MDI);
      var ADX = new OutputExpr("ADX",
          new ExpmemaExpr(
              new MulExpr(
                  new DivExpr(
                      new AbsExpr(
                          new SubExpr(MDI, PDI)
                      ),
                      new AddExpr(MDI, PDI)
                  ),
                  new ConstExpr(100)
              ),
              MM
          )
      );
      this.addOutput(ADX);
      var ADXR = new OutputExpr("ADXR",
          new ExpmemaExpr(ADX, MM)
      );
      this.addOutput(ADXR);
  };
  DMIIndicator.prototype.getName = function () {
      return "DMI";
  };
  var DMAIndicator = create_class(Indicator);
  DMAIndicator.prototype.__construct = function () {
      DMAIndicator.__super.__construct.call(this);
      var N1 = new ParameterExpr("N1", 2, 60, 10);
      var N2 = new ParameterExpr("N2", 2, 250, 50);
      var M = new ParameterExpr("M", 2, 100, 10);
      this.addParameter(N1);
      this.addParameter(N2);
      this.addParameter(M);
      var DIF = new OutputExpr("DIF",
          new SubExpr(
              new MaExpr(new CloseExpr(), N1),
              new MaExpr(new CloseExpr(), N2)
          )
      );
      this.addOutput(DIF);
      var DIFMA = new OutputExpr("DIFMA",
          new MaExpr(DIF, M)
      );
      this.addOutput(DIFMA);
  };
  DMAIndicator.prototype.getName = function () {
      return "DMA";
  };
  var TRIXIndicator = create_class(Indicator);
  TRIXIndicator.prototype.__construct = function () {
      TRIXIndicator.__super.__construct.call(this);
      var N = new ParameterExpr("N", 2, 100, 12);
      var M = new ParameterExpr("M", 2, 100, 9);
      this.addParameter(N);
      this.addParameter(M);
      var MTR = new AssignExpr("MTR",
          new EmaExpr(
              new EmaExpr(
                  new EmaExpr(new CloseExpr(), N), N), N)
      );
      this.addAssign(MTR);
      var TRIX = new OutputExpr("TRIX",
          new MulExpr(
              new DivExpr(
                  new SubExpr(
                      MTR,
                      new RefExpr(
                          MTR,
                          new ConstExpr(1)
                      )
                  ),
                  new RefExpr(
                      MTR,
                      new ConstExpr(1)
                  )
              ),
              new ConstExpr(100)
          )
      );
      this.addOutput(TRIX);
      var MATRIX = new OutputExpr("MATRIX",
          new MaExpr(TRIX, M)
      );
      this.addOutput(MATRIX);
  };
  TRIXIndicator.prototype.getName = function () {
      return "TRIX";
  };
  var BRARIndicator = create_class(Indicator);
  BRARIndicator.prototype.__construct = function () {
      BRARIndicator.__super.__construct.call(this);
      var N = new ParameterExpr("N", 2, 120, 26);
      this.addParameter(N);
      var REF_CLOSE_1 = new AssignExpr("REF_CLOSE_1",
          new RefExpr(new CloseExpr(), new ConstExpr(1))
      );
      this.addAssign(REF_CLOSE_1);
      var BR = new OutputExpr("BR",
          new MulExpr(
              new DivExpr(
                  new SumExpr(
                      new MaxExpr(
                          new ConstExpr(0),
                          new SubExpr(
                              new HighExpr(),
                              REF_CLOSE_1
                          )
                      ),
                      N
                  ),
                  new SumExpr(
                      new MaxExpr(
                          new ConstExpr(0),
                          new SubExpr(
                              REF_CLOSE_1,
                              new LowExpr()
                          )
                      ),
                      N
                  )
              ),
              new ConstExpr(100)
          )
      );
      this.addOutput(BR);
      var AR = new OutputExpr("AR",
          new MulExpr(
              new DivExpr(
                  new SumExpr(
                      new SubExpr(
                          new HighExpr(),
                          new OpenExpr()
                      ),
                      N
                  ),
                  new SumExpr(
                      new SubExpr(
                          new OpenExpr(),
                          new LowExpr()
                      ),
                      N
                  )
              ),
              new ConstExpr(100)
          )
      );
      this.addOutput(AR);
  };
  BRARIndicator.prototype.getName = function () {
      return "BRAR";
  };
  var VRIndicator = create_class(Indicator);
  VRIndicator.prototype.__construct = function () {
      VRIndicator.__super.__construct.call(this);
      var N = new ParameterExpr("N", 2, 100, 26);
      var M = new ParameterExpr("M", 2, 100, 6);
      this.addParameter(N);
      this.addParameter(M);
      var REF_CLOSE_1 = new AssignExpr("REF_CLOSE_1",
          new RefExpr(new CloseExpr(), new ConstExpr(1))
      );
      this.addAssign(REF_CLOSE_1);
      var TH = new AssignExpr("TH",
          new SumExpr(
              new IfExpr(
                  new GtExpr(
                      new CloseExpr(),
                      REF_CLOSE_1
                  ),
                  new VolumeExpr(),
                  new ConstExpr(0)
              ),
              N
          )
      );
      this.addAssign(TH);
      var TL = new AssignExpr("TL",
          new SumExpr(
              new IfExpr(
                  new LtExpr(
                      new CloseExpr(),
                      REF_CLOSE_1
                  ),
                  new VolumeExpr(),
                  new ConstExpr(0)
              ),
              N
          )
      );
      this.addAssign(TL);
      var TQ = new AssignExpr("TQ",
          new SumExpr(
              new IfExpr(
                  new EqExpr(
                      new CloseExpr(),
                      REF_CLOSE_1
                  ),
                  new VolumeExpr(),
                  new ConstExpr(0)
              ),
              N
          )
      );
      this.addAssign(TQ);
      var VR = new OutputExpr("VR",
          new MulExpr(
              new DivExpr(
                  new AddExpr(
                      new MulExpr(
                          TH,
                          new ConstExpr(2)
                      ),
                      TQ
                  ),
                  new AddExpr(
                      new MulExpr(
                          TL,
                          new ConstExpr(2)
                      ),
                      TQ
                  )
              ),
              new ConstExpr(100)
          )
      );
      this.addOutput(VR);
      var MAVR = new OutputExpr("MAVR",
          new MaExpr(VR, M)
      );
      this.addOutput(MAVR);
  };
  VRIndicator.prototype.getName = function () {
      return "VR";
  };
  var OBVIndicator = create_class(Indicator);
  OBVIndicator.prototype.__construct = function () {
      OBVIndicator.__super.__construct.call(this);
      var M = new ParameterExpr("M", 2, 100, 30);
      this.addParameter(M);
      var REF_CLOSE_1 = new AssignExpr("REF_CLOSE_1",
          new RefExpr(new CloseExpr(), new ConstExpr(1))
      );
      this.addAssign(REF_CLOSE_1);
      var VA = new AssignExpr("VA",
          new IfExpr(
              new GtExpr(new CloseExpr(), REF_CLOSE_1),
              new VolumeExpr(),
              new NegExpr(new VolumeExpr())
          )
      );
      this.addAssign(VA);
      var OBV = new OutputExpr("OBV",
          new SumExpr(
              new IfExpr(
                  new EqExpr(new CloseExpr(), REF_CLOSE_1),
                  new ConstExpr(0),
                  VA
              ),
              new ConstExpr(0)
          )
      );
      this.addOutput(OBV);
      var MAOBV = new OutputExpr("MAOBV",
          new MaExpr(OBV, M)
      );
      this.addOutput(MAOBV);
  };
  OBVIndicator.prototype.getName = function () {
      return "OBV";
  };
  var EMVIndicator = create_class(Indicator);
  EMVIndicator.prototype.__construct = function () {
      EMVIndicator.__super.__construct.call(this);
      var N = new ParameterExpr("N", 2, 90, 14);
      var M = new ParameterExpr("M", 2, 60, 9);
      this.addParameter(N);
      this.addParameter(M);
      var VOLUME = new AssignExpr("VOLUME",
          new DivExpr(
              new MaExpr(new VolumeExpr(), N),
              new VolumeExpr()
          )
      );
      this.addAssign(VOLUME);
      var MID = new AssignExpr("MID",
          new MulExpr(
              new DivExpr(
                  new SubExpr(
                      new AddExpr(new HighExpr(), new LowExpr()),
                      new RefExpr(
                          new AddExpr(new HighExpr(), new LowExpr()),
                          new ConstExpr(1)
                      )
                  ),
                  new AddExpr(new HighExpr(), new LowExpr())
              ),
              new ConstExpr(100)
          )
      );
      this.addAssign(MID);
      var EMV = new OutputExpr("EMV",
          new MaExpr(
              new DivExpr(
                  new MulExpr(
                      MID,
                      new MulExpr(
                          VOLUME,
                          new SubExpr(new HighExpr(), new LowExpr())
                      )
                  ),
                  new MaExpr(
                      new SubExpr(new HighExpr(), new LowExpr()),
                      N
                  )
              ),
              N
          )
      );
      this.addOutput(EMV);
      var MAEMV = new OutputExpr("MAEMV",
          new MaExpr(EMV, M)
      );
      this.addOutput(MAEMV);
  };
  EMVIndicator.prototype.getName = function () {
      return "EMV";
  };
  var RSIIndicator = create_class(Indicator);
  RSIIndicator.prototype.__construct = function () {
      RSIIndicator.__super.__construct.call(this);
      var N1 = new ParameterExpr("N1", 2, 120, 6);
      var N2 = new ParameterExpr("N2", 2, 250, 12);
      var N3 = new ParameterExpr("N3", 2, 500, 24);
      this.addParameter(N1);
      this.addParameter(N2);
      this.addParameter(N3);
      var LC = new AssignExpr("LC",
          new RefExpr(new CloseExpr(), new ConstExpr(1))
      );
      this.addAssign(LC);
      var CLOSE_LC = new AssignExpr("CLOSE_LC",
          new SubExpr(new CloseExpr(), LC)
      );
      this.addAssign(CLOSE_LC);
      this.addOutput(new OutputExpr("RSI1",
          new MulExpr(
              new DivExpr(
                  new SmaExpr(new MaxExpr(CLOSE_LC, new ConstExpr(0)), N1, new ConstExpr(1)),
                  new SmaExpr(new AbsExpr(CLOSE_LC), N1, new ConstExpr(1))
              ),
              new ConstExpr(100)
          )
      ));
      this.addOutput(new OutputExpr("RSI2",
          new MulExpr(
              new DivExpr(
                  new SmaExpr(new MaxExpr(CLOSE_LC, new ConstExpr(0)), N2, new ConstExpr(1)),
                  new SmaExpr(new AbsExpr(CLOSE_LC), N2, new ConstExpr(1))
              ),
              new ConstExpr(100)
          )
      ));
      this.addOutput(new OutputExpr("RSI3",
          new MulExpr(
              new DivExpr(
                  new SmaExpr(new MaxExpr(CLOSE_LC, new ConstExpr(0)), N3, new ConstExpr(1)),
                  new SmaExpr(new AbsExpr(CLOSE_LC), N3, new ConstExpr(1))
              ),
              new ConstExpr(100)
          )
      ));
  };
  RSIIndicator.prototype.getName = function () {
      return "RSI";
  };
  var WRIndicator = create_class(Indicator);
  WRIndicator.prototype.__construct = function () {
      WRIndicator.__super.__construct.call(this);
      var N = new ParameterExpr("N", 2, 100, 10);
      var N1 = new ParameterExpr("N1", 2, 100, 6);
      this.addParameter(N);
      this.addParameter(N1);
      var HHV = new AssignExpr("HHV",
          new HhvExpr(new HighExpr(), N)
      );
      this.addAssign(HHV);
      var HHV1 = new AssignExpr("HHV1",
          new HhvExpr(new HighExpr(), N1)
      );
      this.addAssign(HHV1);
      var LLV = new AssignExpr("LLV",
          new LlvExpr(new LowExpr(), N)
      );
      this.addAssign(LLV);
      var LLV1 = new AssignExpr("LLV1",
          new LlvExpr(new LowExpr(), N1)
      );
      this.addAssign(LLV1);
      var WR1 = new OutputExpr("WR1",
          new MulExpr(
              new DivExpr(
                  new SubExpr(
                      HHV,
                      new CloseExpr()
                  ),
                  new SubExpr(
                      HHV,
                      LLV
                  )
              ),
              new ConstExpr(100)
          )
      );
      this.addOutput(WR1);
      var WR2 = new OutputExpr("WR2",
          new MulExpr(
              new DivExpr(
                  new SubExpr(
                      HHV1,
                      new CloseExpr()
                  ),
                  new SubExpr(
                      HHV1,
                      LLV1
                  )
              ),
              new ConstExpr(100)
          )
      );
      this.addOutput(WR2);
  };
  WRIndicator.prototype.getName = function () {
      return "WR";
  };
  var SARIndicator = create_class(Indicator);
  SARIndicator.prototype.__construct = function () {
      SARIndicator.__super.__construct.call(this);
      var N = new ConstExpr(4);
      var MIN = new ConstExpr(2);
      var STEP = new ConstExpr(2);
      var MAX = new ConstExpr(20);
      this.addOutput(new OutputExpr("SAR",
          new SarExpr(N, MIN, STEP, MAX),
          OutputStyle.SARPoint
      ));
  };
  SARIndicator.prototype.getName = function () {
      return "SAR";
  };
  var KDJIndicator = create_class(Indicator);
  KDJIndicator.prototype.__construct = function () {
      KDJIndicator.__super.__construct.call(this);
      var N = new ParameterExpr("N", 2, 90, 9);
      var M1 = new ParameterExpr("M1", 2, 30, 3);
      var M2 = new ParameterExpr("M2", 2, 30, 3);
      this.addParameter(N);
      this.addParameter(M1);
      this.addParameter(M2);
      var HHV = new AssignExpr("HHV",
          new HhvExpr(new HighExpr(), N)
      );
      this.addAssign(HHV);
      var LLV = new AssignExpr("LLV",
          new LlvExpr(new LowExpr(), N)
      );
      this.addAssign(LLV);
      var RSV = new AssignExpr("RSV",
          new MulExpr(
              new DivExpr(
                  new SubExpr(
                      new CloseExpr(),
                      LLV
                  ),
                  new SubExpr(
                      HHV,
                      LLV
                  )
              ),
              new ConstExpr(100)
          )
      );
      this.addAssign(RSV);
      var K = new OutputExpr("K",
          new SmaExpr(RSV, M1, new ConstExpr(1))
      );
      this.addOutput(K);
      var D = new OutputExpr("D",
          new SmaExpr(K, M2, new ConstExpr(1))
      );
      this.addOutput(D);
      var J = new OutputExpr("J",
          new SubExpr(
              new MulExpr(
                  K,
                  new ConstExpr(3)
              ),
              new MulExpr(
                  D,
                  new ConstExpr(2)
              )
          )
      );
      this.addOutput(J);
  };
  KDJIndicator.prototype.getName = function () {
      return "KDJ";
  };
  var ROCIndicator = create_class(Indicator);
  ROCIndicator.prototype.__construct = function () {
      ROCIndicator.__super.__construct.call(this);
      var N = new ParameterExpr("N", 2, 120, 12);
      var M = new ParameterExpr("M", 2, 60, 6);
      this.addParameter(N);
      this.addParameter(M);
      var REF_CLOSE_N = new AssignExpr("REF_CLOSE_N",
          new RefExpr(new CloseExpr(), N)
      );
      this.addAssign(REF_CLOSE_N);
      var ROC = new OutputExpr("ROC",
          new MulExpr(
              new DivExpr(
                  new SubExpr(
                      new CloseExpr(),
                      REF_CLOSE_N
                  ),
                  REF_CLOSE_N
              ),
              new ConstExpr(100)
          )
      );
      this.addOutput(ROC);
      var MAROC = new OutputExpr("MAROC",
          new MaExpr(ROC, M)
      );
      this.addOutput(MAROC);
  };
  ROCIndicator.prototype.getName = function () {
      return "ROC";
  };
  var MTMIndicator = create_class(Indicator);
  MTMIndicator.prototype.__construct = function () {
      MTMIndicator.__super.__construct.call(this);
      var N = new ParameterExpr("N", 2, 120, 12);
      var M = new ParameterExpr("M", 2, 60, 6);
      this.addParameter(N);
      this.addParameter(M);
      var MTM = new OutputExpr("MTM",
          new SubExpr(
              new CloseExpr(),
              new RefExpr(new CloseExpr(), N)
          )
      );
      this.addOutput(MTM);
      var MTMMA = new OutputExpr("MTMMA",
          new MaExpr(MTM, M)
      );
      this.addOutput(MTMMA);
  };
  MTMIndicator.prototype.getName = function () {
      return "MTM";
  };
  var BOLLIndicator = create_class(Indicator);
  BOLLIndicator.prototype.__construct = function () {
      BOLLIndicator.__super.__construct.call(this);
      var N = new ParameterExpr("N", 2, 120, 20);
      this.addParameter(N);
      var STD_CLOSE_N = new AssignExpr("STD_CLOSE_N",
          new StdExpr(new CloseExpr(), N)
      );
      this.addAssign(STD_CLOSE_N);
      var BOLL = new OutputExpr("BOLL",
          new MaExpr(new CloseExpr(), N)
      );
      this.addOutput(BOLL);
      var UB = new OutputExpr("UB",
          new AddExpr(
              BOLL,
              new MulExpr(
                  new ConstExpr(2),
                  STD_CLOSE_N
              )
          )
      );
      this.addOutput(UB);
      var LB = new OutputExpr("LB",
          new SubExpr(
              BOLL,
              new MulExpr(
                  new ConstExpr(2),
                  STD_CLOSE_N
              )
          )
      );
      this.addOutput(LB);
  };
  BOLLIndicator.prototype.getName = function () {
      return "BOLL";
  };
  var PSYIndicator = create_class(Indicator);
  PSYIndicator.prototype.__construct = function () {
      PSYIndicator.__super.__construct.call(this);
      var N = new ParameterExpr("N", 2, 100, 12);
      var M = new ParameterExpr("M", 2, 100, 6);
      this.addParameter(N);
      this.addParameter(M);
      var PSY = new OutputExpr("PSY",
          new MulExpr(
              new DivExpr(
                  new CountExpr(
                      new GtExpr(
                          new CloseExpr(),
                          new RefExpr(new CloseExpr(), new ConstExpr(1))
                      ),
                      N
                  ),
                  N
              ),
              new ConstExpr(100)
          )
      );
      this.addOutput(PSY);
      var PSYMA = new OutputExpr("PSYMA",
          new MaExpr(PSY, M)
      );
      this.addOutput(PSYMA);
  };
  PSYIndicator.prototype.getName = function () {
      return "PSY";
  };
  var STOCHRSIIndicator = create_class(Indicator);
  STOCHRSIIndicator.prototype.__construct = function () {
      STOCHRSIIndicator.__super.__construct.call(this);
      var N = new ParameterExpr("N", 3, 100, 14);
      var M = new ParameterExpr("M", 3, 100, 14);
      var P1 = new ParameterExpr("P1", 2, 50, 3);
      var P2 = new ParameterExpr("P2", 2, 50, 3);
      this.addParameter(N);
      this.addParameter(M);
      this.addParameter(P1);
      this.addParameter(P2);
      var LC = new AssignExpr("LC",
          new RefExpr(new CloseExpr(), new ConstExpr(1))
      );
      this.addAssign(LC);
      var CLOSE_LC = new AssignExpr("CLOSE_LC",
          new SubExpr(new CloseExpr(), LC)
      );
      this.addAssign(CLOSE_LC);
      var RSI = new AssignExpr("RSI",
          new MulExpr(
              new DivExpr(
                  new SmaExpr(new MaxExpr(CLOSE_LC, new ConstExpr(0)), N, new ConstExpr(1)),
                  new SmaExpr(new AbsExpr(CLOSE_LC), N, new ConstExpr(1))
              ),
              new ConstExpr(100)
          )
      );
      this.addAssign(RSI);
      var STOCHRSI = new OutputExpr("STOCHRSI",
          new MulExpr(
              new DivExpr(
                  new MaExpr(
                      new SubExpr(
                          RSI,
                          new LlvExpr(RSI, M)
                      ),
                      P1
                  ),
                  new MaExpr(
                      new SubExpr(
                          new HhvExpr(RSI, M),
                          new LlvExpr(RSI, M)
                      ),
                      P1
                  )
              ),
              new ConstExpr(100)
          )
      );
      this.addOutput(STOCHRSI);
      this.addOutput(new RangeOutputExpr("MA",
          new MaExpr(STOCHRSI, P2)
      ));
  };
  STOCHRSIIndicator.prototype.getName = function () {
      return "StochRSI";
  };
  /**
   * Created by Administrator on 2014/11/19.
   */
  var Chart = create_class();
  Chart.strPeriod = {
      'zh-cn': {
          'line': '(分时)',
          '1min': '(1分钟)',
          '5min': '(5分钟)',
          '15min': '(15分钟)',
          '30min': '(30分钟)',
          '1hour': '(1小时)',
          '1day': '(日线)',
          '1week': '(周线)',
          '3min': '(3分钟)',
          '2hour': '(2小时)',
          '4hour': '(4小时)',
          '6hour': '(6小时)',
          '12hour': '(12小时)',
          '3day': '(3天)'
      },
      'en-us': {
          'line': '(Line)',
          '1min': '(1m)',
          '5min': '(5m)',
          '15min': '(15m)',
          '30min': '(30m)',
          '1hour': '(1h)',
          '1day': '(1d)',
          '1week': '(1w)',
          '3min': '(3m)',
          '2hour': '(2h)',
          '4hour': '(4h)',
          '6hour': '(6h)',
          '12hour': '(12h)',
          '3day': '(3d)'
      },
      'zh-tw': {
          'line': '(分時)',
          '1min': '(1分钟)',
          '5min': '(5分钟)',
          '15min': '(15分钟)',
          '30min': '(30分钟)',
          '1hour': '(1小時)',
          '1day': '(日线)',
          '1week': '(周线)',
          '3min': '(3分钟)',
          '2hour': '(2小時)',
          '4hour': '(4小時)',
          '6hour': '(6小時)',
          '12hour': '(12小時)',
          '3day': '(3天)'
      }
  };
  Chart.prototype.__construct = function () {
      this._data = null;
      this._charStyle = "CandleStick";
      this._depthData = {
          array: null,
          asks_count: 0,
          bids_count: 0,
          asks_si: 0,
          asks_ei: 0,
          bids_si: 0,
          bids_ei: 0
      };
      this._range = KlineIns.range;
      this._symbol = KlineIns.symbol;
  };
  Chart.prototype.setTitle = function () {
      var lang = ChartManager.getInstance().getLanguage();
      var title = KlineIns.symbolName;
      title += ' ';
      title += this.strIsLine ? Chart.strPeriod[lang]['line'] : Chart.strPeriod[lang][this._range];
      title += (this._contract_unit + '/' + this._money_type).toUpperCase();
      ChartManager.getInstance().setTitle('frame0.k0', title);
  };
  Chart.prototype.setSymbol = function (symbol) {
      this._symbol = symbol;
      this.updateDataAndDisplay();
  };
  
  Chart.prototype.updateDataAndDisplay = function () {
      KlineIns.symbol = this._symbol;
      KlineIns.range = this._range;
      ChartManager.getInstance().setCurrentDataSource('frame0.k0', this._symbol + '.' + this._range);
      ChartManager.getInstance().setNormalMode();
      var f = KlineIns.chartMgr.getDataSource("frame0.k0").getLastDate();
  
      $('.symbol-title>a').text(KlineIns.symbolName);
  
      if (f == -1) {
          KlineIns.requestParam = setHttpRequestParam(KlineIns.symbol, KlineIns.range, KlineIns.limit, null);
          RequestData(true);
      } else {
          KlineIns.requestParam = setHttpRequestParam(KlineIns.symbol, KlineIns.range, null, f.toString());
          RequestData();
      }
      ChartManager.getInstance().redraw('All', false);
  };
  Chart.prototype.setCurrentContractUnit = function (contractUnit) {
      this._contract_unit = contractUnit;
      this.updateDataAndDisplay();
  };
  Chart.prototype.setCurrentMoneyType = function (moneyType) {
      this._money_type = moneyType;
      this.updateDataAndDisplay();
  };
  Chart.prototype.setCurrentPeriod = function (period) {
      this._range = KlineIns.periodMap[period];
      this.updateDataAndDisplay();
      KlineIns.onRangeChange(this._range);
  };
  Chart.prototype.updateDataSource = function (data) {
      this._data = data;
      ChartManager.getInstance().updateData("frame0.k0", this._data);
  };
  Chart.prototype.updateDepth = function (array) {
      if (array == null) {
          this._depthData.array = [];
          ChartManager.getInstance().redraw('All', false);
          return;
      }
      if (!array.asks || !array.bids || array.asks == '' || array.bids == '')
          return;
      var _data = this._depthData;
      _data.array = [];
      for (var i = 0; i < array.asks.length; i++) {
          var data = {};
          data.rate = array.asks[i][0];
          data.amount = array.asks[i][1];
          _data.array.push(data);
      }
      for (var i = 0; i < array.bids.length; i++) {
          var data = {};
          data.rate = array.bids[i][0];
          data.amount = array.bids[i][1];
          _data.array.push(data);
      }
      _data.asks_count = array.asks.length;
      _data.bids_count = array.bids.length;
      _data.asks_si = _data.asks_count - 1;
      _data.asks_ei = 0;
      _data.bids_si = _data.asks_count;
      _data.bids_ei = _data.asks_count + _data.bids_count - 1;
      for (var i = _data.asks_si; i >= _data.asks_ei; i--) {
          if (i == _data.asks_si) {
              _data.array[i].amounts = _data.array[i].amount;
          } else {
              _data.array[i].amounts = _data.array[i + 1].amounts + _data.array[i].amount;
          }
      }
      for (var i = _data.bids_si; i <= _data.bids_ei; i++) {
          if (i == _data.bids_si) {
              _data.array[i].amounts = _data.array[i].amount;
          } else {
              _data.array[i].amounts = _data.array[i - 1].amounts + _data.array[i].amount;
          }
      }
      ChartManager.getInstance().redraw('All', false);
  };
  Chart.prototype.setMainIndicator = function (indicName) {
      this._mainIndicator = indicName;
      if (indicName == 'NONE') {
          ChartManager.getInstance().removeMainIndicator('frame0.k0');
      } else {
          ChartManager.getInstance().setMainIndicator('frame0.k0', indicName);
      }
      ChartManager.getInstance().redraw('All', true);
  };
  Chart.prototype.setIndicator = function (index, indicName) {
      if (indicName == 'NONE') {
          var index = 2;
          if (Template.displayVolume == false)
              index = 1;
          var areaName = ChartManager.getInstance().getIndicatorAreaName('frame0.k0', index);
          if (areaName != '')
              ChartManager.getInstance().removeIndicator(areaName);
      } else {
          var index = 2;
          if (Template.displayVolume == false)
              index = 1;
          var areaName = ChartManager.getInstance().getIndicatorAreaName('frame0.k0', index);
          if (areaName == '') {
              Template.createIndicatorChartComps('frame0.k0', indicName);
          } else {
              ChartManager.getInstance().setIndicator(areaName, indicName);
          }
      }
      ChartManager.getInstance().redraw('All', true);
  };
  Chart.prototype.addIndicator = function (indicName) {
      ChartManager.getInstance().addIndicator(indicName);
      ChartManager.getInstance().redraw('All', true);
  };
  Chart.prototype.removeIndicator = function (indicName) {
      var areaName = ChartManager.getInstance().getIndicatorAreaName(2);
      ChartManager.getInstance().removeIndicator(areaName);
      ChartManager.getInstance().redraw('All', true);
  };
  var CName = create_class();
  CName.prototype.__construct = function (name) {
      this._names = [];
      this._comps = [];
      if (name instanceof CName) {
          this._names = name._names;
          this._comps = name._comps;
      } else {
          var comps = name.split(".");
          var dotNum = comps.length - 1;
          if (dotNum > 0) {
              this._comps = comps;
              this._names.push(comps[0]);
              for (var i = 1; i <= dotNum; i++) {
                  this._names.push(this._names[i - 1] + "." + comps[i]);
              }
          } else {
              this._comps.push(name);
              this._names.push(name);
          }
      }
  };
  CName.prototype.getCompAt = function (index) {
      if (index >= 0 && index < this._comps.length)
          return this._comps[index];
      return "";
  };
  CName.prototype.getName = function (index) {
      if (index < 0) {
          if (this._names.length > 0)
              return this._names[this._names.length - 1];
      } else if (index < this._names.length) {
          return this._names[index];
      }
      return "";
  };
  var NamedObject = create_class();
  NamedObject.prototype.__construct = function (name) {
      this._name = name;
      this._nameObj = new CName(name);
  };
  NamedObject.prototype.getFrameName = function () {
      return this._nameObj.getName(0);
  };
  NamedObject.prototype.getDataSourceName = function () {
      return this._nameObj.getName(1);
  };
  NamedObject.prototype.getAreaName = function () {
      return this._nameObj.getName(2);
  };
  NamedObject.prototype.getName = function () {
      return this._nameObj.getName(-1);
  };
  NamedObject.prototype.getNameObject = function () {
      return this._nameObj;
  };
  var ChartArea = create_class(NamedObject);
  ChartArea.prototype.__construct = function (name) {
      ChartArea.__super.__construct.call(this, name);
      this._left = 0;
      this._top = 0;
      this._right = 0;
      this._bottom = 0;
      this._changed = false;
      this._highlighted = false;
      this._pressed = false;
      this._selected = false;
      this.Measuring = new MEvent();
  };
  ChartArea.DockStyle = {Left: 0, Top: 1, Right: 2, Bottom: 3, Fill: 4};
  ChartArea.prototype.getDockStyle = function () {
      return this._dockStyle;
  };
  ChartArea.prototype.setDockStyle = function (dockStyle) {
      this._dockStyle = dockStyle;
  };
  ChartArea.prototype.getLeft = function () {
      return this._left;
  };
  ChartArea.prototype.getTop = function () {
      return this._top;
  };
  ChartArea.prototype.setTop = function (v) {
      if (this._top != v) {
          this._top = v;
          this._changed = true;
      }
  };
  ChartArea.prototype.getRight = function () {
      return this._right;
  };
  ChartArea.prototype.getBottom = function () {
      return this._bottom;
  };
  ChartArea.prototype.setBottom = function (v) {
      if (this._bottom != v) {
          this._bottom = v;
          this._changed = true;
      }
  };
  ChartArea.prototype.getCenter = function () {
      return (this._left + this._right) >> 1;
  };
  ChartArea.prototype.getMiddle = function () {
      return (this._top + this._bottom) >> 1;
  };
  ChartArea.prototype.getWidth = function () {
      return this._right - this._left;
  };
  ChartArea.prototype.getHeight = function () {
      return this._bottom - this._top;
  };
  ChartArea.prototype.getRect = function () {
      return {
          X: this._left,
          Y: this._top,
          Width: this._right - this._left,
          Height: this._bottom - this._top
      };
  };
  ChartArea.prototype.contains = function (x, y) {
      if (x >= this._left && x < this._right)
          if (y >= this._top && y < this._bottom)
              return [this];
      return null;
  };
  ChartArea.prototype.getMeasuredWidth = function () {
      return this._measuredWidth;
  };
  ChartArea.prototype.getMeasuredHeight = function () {
      return this._measuredHeight;
  };
  ChartArea.prototype.setMeasuredDimension = function (width, height) {
      this._measuredWidth = width;
      this._measuredHeight = height;
  };
  ChartArea.prototype.measure = function (context, width, height) {
      this._measuredWidth = 0;
      this._measuredHeight = 0;
      this.Measuring.raise(this, {Width: width, Height: height});
      if (this._measuredWidth == 0 && this._measuredHeight == 0)
          this.setMeasuredDimension(width, height);
  };
  ChartArea.prototype.layout = function (left, top, right, bottom, forceChange) {
      left <<= 0;
      if (this._left != left) {
          this._left = left;
          this._changed = true;
      }
      top <<= 0;
      if (this._top != top) {
          this._top = top;
          this._changed = true;
      }
      right <<= 0;
      if (this._right != right) {
          this._right = right;
          this._changed = true;
      }
      bottom <<= 0;
      if (this._bottom != bottom) {
          this._bottom = bottom;
          this._changed = true;
      }
      if (forceChange)
          this._changed = true;
  };
  ChartArea.prototype.isChanged = function () {
      return this._changed;
  };
  ChartArea.prototype.setChanged = function (v) {
      this._changed = v;
  };
  ChartArea.prototype.isHighlighted = function () {
      return this._highlighted;
  };
  ChartArea.prototype.getHighlightedArea = function () {
      return this._highlighted ? this : null;
  };
  ChartArea.prototype.highlight = function (area) {
      this._highlighted = (this == area);
      return this._highlighted ? this : null;
  };
  ChartArea.prototype.isPressed = function () {
      return this._pressed;
  };
  ChartArea.prototype.setPressed = function (v) {
      this._pressed = v;
  };
  ChartArea.prototype.isSelected = function () {
      return this._selected;
  };
  ChartArea.prototype.getSelectedArea = function () {
      return this._selected ? this : null;
  };
  ChartArea.prototype.select = function (area) {
      this._selected = (this == area);
      return this._selected ? this : null;
  };
  ChartArea.prototype.onMouseMove = function (x, y) {
      return null;
  };
  ChartArea.prototype.onMouseLeave = function (x, y) {
  };
  ChartArea.prototype.onMouseDown = function (x, y) {
      return null;
  };
  ChartArea.prototype.onMouseUp = function (x, y) {
      return null;
  };
  var MainArea = create_class(ChartArea);
  MainArea.prototype.__construct = function (name) {
      MainArea.__super.__construct.call(this, name);
      this._dragStarted = false;
      this._oldX = 0;
      this._oldY = 0;
      this._passMoveEventToToolManager = true;
  };
  MainArea.prototype.onMouseMove = function (x, y) {
      var mgr = ChartManager.getInstance();
      if (mgr._capturingMouseArea == this)
          if (this._dragStarted == false)
              if (Math.abs(this._oldX - x) > 1 || Math.abs(this._oldY - y) > 1)
                  this._dragStarted = true;
      if (this._dragStarted) {
          mgr.hideCursor();
          if (mgr.onToolMouseDrag(this.getFrameName(), x, y))
              return this;
          mgr.getTimeline(this.getDataSourceName()).move(x - this._oldX);
          return this;
      }
      if (this._passMoveEventToToolManager && mgr.onToolMouseMove(this.getFrameName(), x, y)) {
          mgr.hideCursor();
          return this;
      }
      switch (mgr._drawingTool) {
          case ChartManager.DrawingTool.Cursor:
              mgr.showCursor();
              break;
          case ChartManager.DrawingTool.CrossCursor:
              if (mgr.showCrossCursor(this, x, y))
                  mgr.hideCursor();
              else
                  mgr.showCursor();
              break;
          default:
              mgr.hideCursor();
              break;
      }
      return this;
  };
  MainArea.prototype.onMouseLeave = function (x, y) {
      this._dragStarted = false;
      this._passMoveEventToToolManager = true;
  };
  MainArea.prototype.onMouseDown = function (x, y) {
      var mgr = ChartManager.getInstance();
      mgr.getTimeline(this.getDataSourceName()).startMove();
      this._oldX = x;
      this._oldY = y;
      this._dragStarted = false;
      if (mgr.onToolMouseDown(this.getFrameName(), x, y))
          this._passMoveEventToToolManager = false;
      return this;
  };
  MainArea.prototype.onMouseUp = function (x, y) {
      var mgr = ChartManager.getInstance();
      var ret = null;
      if (this._dragStarted) {
          this._dragStarted = false;
          ret = this;
      }
      if (mgr.onToolMouseUp(this.getFrameName(), x, y))
          ret = this;
      this._passMoveEventToToolManager = true;
      return ret;
  };
  var IndicatorArea = create_class(ChartArea);
  IndicatorArea.prototype.__construct = function (name) {
      IndicatorArea.__super.__construct.call(this, name);
      this._dragStarted = false;
      this._oldX = 0;
      this._oldY = 0;
  };
  IndicatorArea.prototype.onMouseMove = function (x, y) {
      var mgr = ChartManager.getInstance();
      if (mgr._capturingMouseArea == this)
          if (this._dragStarted == false)
              if (this._oldX != x || this._oldY != y)
                  this._dragStarted = true;
      if (this._dragStarted) {
          mgr.hideCursor();
          mgr.getTimeline(this.getDataSourceName()).move(x - this._oldX);
          return this;
      }
      switch (mgr._drawingTool) {
          case ChartManager.DrawingTool.CrossCursor:
              if (mgr.showCrossCursor(this, x, y))
                  mgr.hideCursor();
              else
                  mgr.showCursor();
              break;
          default:
              mgr.showCursor();
              break;
      }
      return this;
  };
  IndicatorArea.prototype.onMouseLeave = function (x, y) {
      this._dragStarted = false;
  };
  IndicatorArea.prototype.onMouseDown = function (x, y) {
      var mgr = ChartManager.getInstance();
      mgr.getTimeline(this.getDataSourceName()).startMove();
      this._oldX = x;
      this._oldY = y;
      this._dragStarted = false;
      return this;
  };
  IndicatorArea.prototype.onMouseUp = function (x, y) {
      if (this._dragStarted) {
          this._dragStarted = false;
          return this;
      }
      return null;
  };
  var MainRangeArea = create_class(ChartArea);
  MainRangeArea.prototype.__construct = function (name) {
      MainRangeArea.__super.__construct.call(this, name);
  };
  MainRangeArea.prototype.onMouseMove = function (x, y) {
      ChartManager.getInstance().showCursor();
      return this;
  };
  var IndicatorRangeArea = create_class(ChartArea);
  IndicatorRangeArea.prototype.__construct = function (name) {
      IndicatorRangeArea.__super.__construct.call(this, name);
  };
  IndicatorRangeArea.prototype.onMouseMove = function (x, y) {
      ChartManager.getInstance().showCursor();
      return this;
  };
  var TimelineArea = create_class(ChartArea);
  TimelineArea.prototype.__construct = function (name) {
      TimelineArea.__super.__construct.call(this, name);
  };
  TimelineArea.prototype.onMouseMove = function (x, y) {
      ChartManager.getInstance().showCursor();
      return this;
  };
  var ChartAreaGroup = create_class(ChartArea);
  ChartAreaGroup.prototype.__construct = function (name) {
      ChartAreaGroup.__super.__construct.call(this, name);
      this._areas = [];
      this._highlightedArea = null;
      this._selectedArea = null;
  };
  ChartAreaGroup.prototype.contains = function (x, y) {
      var areas;
      var a, i, cnt = this._areas.length;
      for (i = 0; i < cnt; i++) {
          a = this._areas[i];
          areas = a.contains(x, y);
          if (areas != null) {
              areas.push(this);
              return areas;
          }
      }
      return ChartAreaGroup.__super.contains(x, y);
  };
  ChartAreaGroup.prototype.getAreaCount = function () {
      return this._areas.length;
  };
  ChartAreaGroup.prototype.getAreaAt = function (index) {
      if (index < 0 || index >= this._areas.length)
          return null;
      return this._areas[index];
  };
  ChartAreaGroup.prototype.addArea = function (area) {
      this._areas.push(area);
  };
  ChartAreaGroup.prototype.removeArea = function (area) {
      var i, cnt = this._areas.length;
      for (i = 0; i < cnt; i++) {
          if (area == this._areas[i]) {
              this._areas.splice(i);
              this.setChanged(true);
              break;
          }
      }
  };
  ChartAreaGroup.prototype.getGridColor = function () {
      return this._gridColor;
  };
  ChartAreaGroup.prototype.setGridColor = function (c) {
      this._gridColor = c;
  };
  ChartAreaGroup.prototype.getHighlightedArea = function () {
      if (this._highlightedArea != null)
          return this._highlightedArea.getHighlightedArea();
      return null;
  };
  ChartAreaGroup.prototype.highlight = function (area) {
      this._highlightedArea = null;
      var e, i, cnt = this._areas.length;
      for (i = 0; i < cnt; i++) {
          e = this._areas[i].highlight(area);
          if (e != null) {
              this._highlightedArea = e;
              return this;
          }
      }
      return null;
  };
  ChartAreaGroup.prototype.getSelectedArea = function () {
      if (this._selectedArea != null)
          return this._selectedArea.getSelectedArea();
      return null;
  };
  ChartAreaGroup.prototype.select = function (area) {
      this._selectedArea = null;
      var e, i, cnt = this._areas.length;
      for (i = 0; i < cnt; i++) {
          e = this._areas[i].select(area);
          if (e != null) {
              this._selectedArea = e;
              return this;
          }
      }
      return null;
  };
  ChartAreaGroup.prototype.onMouseLeave = function (x, y) {
      var i, cnt = this._areas.length;
      for (i = 0; i < cnt; i++)
          this._areas[i].onMouseLeave(x, y);
  };
  ChartAreaGroup.prototype.onMouseUp = function (x, y) {
      var a, i, cnt = this._areas.length;
      for (i = 0; i < cnt; i++) {
          a = this._areas[i].onMouseUp(x, y);
          if (a != null)
              return a;
      }
      return null;
  };
  var TableLayout = create_class(ChartAreaGroup);
  TableLayout.prototype.__construct = function (name) {
      TableLayout.__super.__construct.call(this, name);
      this._nextRowId = 0;
      this._focusedRowIndex = -1;
  };
  TableLayout.prototype.getNextRowId = function () {
      return this._nextRowId++;
  };
  TableLayout.prototype.measure = function (context, width, height) {
      this.setMeasuredDimension(width, height);
      var rowH, prevH = 0,
          totalH = 0;
      var h, rows;
      var rh = [];
      var i, cnt = this._areas.length;
      for (i = 0; i < cnt; i += 2) {
          rowH = this._areas[i].getHeight();
          if (rowH == 0) {
              if (i == 0) {
                  rows = (cnt + 1) >> 1;
                  var n = (rows * 2) + 5;
                  var nh = ((height / n) * 2) << 0;
                  h = height;
                  for (i = rows - 1; i > 0; i--) {
                      rh.unshift(nh);
                      h -= nh;
                  }
                  rh.unshift(h);
                  break;
              } else if (i == 2) {
                  rowH = prevH / 3;
              } else {
                  rowH = prevH;
              }
          }
          totalH += rowH;
          prevH = rowH;
          rh.push(rowH);
      }
      if (totalH > 0) {
          var rate = height / totalH;
          rows = (cnt + 1) >> 1;
          h = height;
          for (i = rows - 1; i > 0; i--) {
              rh[i] *= rate;
              h -= rh[i];
          }
          rh[0] = h;
      }
      var nw = 8;
      // chart depths sidebar
      var minRW = 64;
      var maxRW = Math.min(240, width >> 1);
      var rw = minRW;
      var mgr = ChartManager.getInstance();
      var timeline = mgr.getTimeline(this.getDataSourceName());
      if (timeline.getFirstIndex() >= 0) {
          var firstIndexes = [];
          for (rw = minRW; rw < maxRW; rw += nw)
              firstIndexes.push(
                  timeline.calcFirstIndex(timeline.calcColumnCount(width - rw)));
          var lastIndex = timeline.getLastIndex();
          var dpNames = [".main", ".secondary"];
          var minmaxes = new Array(firstIndexes.length);
          var iArea, iIndex;
          for (iArea = 0, iIndex = 0, rw = minRW; iArea < this._areas.length && iIndex < firstIndexes.length; iArea += 2) {
              var area = this._areas[iArea];
              var plotter = mgr.getPlotter(area.getName() + "Range.main");
              for (var iDp in dpNames) {
                  var dp = mgr.getDataProvider(area.getName() + dpNames[iDp]);
                  if (dp == undefined)
                      continue;
                  dp.calcRange(firstIndexes, lastIndex, minmaxes, null);
                  while (iIndex < firstIndexes.length) {
                      var minW = plotter.getRequiredWidth(context, minmaxes[iIndex].min);
                      var maxW = plotter.getRequiredWidth(context, minmaxes[iIndex].max);
                      if (Math.max(minW, maxW) < rw)
                          break;
                      iIndex++;
                      rw += nw;
                  }
              }
          }
      }
      for (i = 1; i < this._areas.length; i += 2) {
          this._areas[i].measure(context, rw, rh[i >> 1]);
      }
      var lw = width - rw;
      for (i = 0; i < this._areas.length; i += 2) {
          this._areas[i].measure(context, lw, rh[i >> 1]);
      }
  };
  TableLayout.prototype.layout = function (left, top, right, bottom, forceChange) {
      TableLayout.__super.layout.call(this, left, top, right, bottom, forceChange);
      if (this._areas.length < 1)
          return;
      var area;
      var center = left + this._areas[0].getMeasuredWidth();
      var t = top,
          b;
      if (!forceChange)
          forceChange = this.isChanged();
      var i, cnt = this._areas.length;
      for (i = 0; i < cnt; i++) {
          area = this._areas[i];
          b = t + area.getMeasuredHeight();
          area.layout(left, t, center, b, forceChange);
          i++;
          area = this._areas[i];
          area.layout(center, t, this.getRight(), b, forceChange);
          t = b;
      }
      this.setChanged(false);
  };
  TableLayout.prototype.drawGrid = function (context) {
      if (this._areas.length < 1)
          return;
      var mgr = ChartManager.getInstance();
      var theme = mgr.getTheme(this.getFrameName());
      context.fillStyle = theme.getColor(Theme.Color.Grid1);
      context.fillRect(this._areas[0].getRight(), this.getTop(), 1, this.getHeight());
      var i, cnt = this._areas.length - 2;
      for (i = 0; i < cnt; i += 2)
          context.fillRect(this.getLeft(), this._areas[i].getBottom(), this.getWidth(), 1);
      if (!mgr.getCaptureMouseWheelDirectly()) {
          for (i = 0, cnt += 2; i < cnt; i += 2) {
              if (this._areas[i].isSelected()) {
                  context.strokeStyle = theme.getColor(Theme.Color.Indicator1);
                  context.strokeRect(
                      this.getLeft() + 0.5, this.getTop() + 0.5,
                      this.getWidth() - 1, this.getHeight() - 1);
                  break;
              }
          }
      }
  };
  TableLayout.prototype.highlight = function (area) {
      this._highlightedArea = null;
      var e, i, cnt = this._areas.length;
      for (i = 0; i < cnt; i++) {
          e = this._areas[i];
          if (e == area) {
              i &= ~1;
              e = this._areas[i];
              e.highlight(e);
              this._highlightedArea = e;
              i++;
              e = this._areas[i];
              e.highlight(null);
              e.highlight(e);
          } else {
              e.highlight(null);
          }
      }
      return this._highlightedArea != null ? this : null;
  };
  TableLayout.prototype.select = function (area) {
      this._selectedArea = null;
      var e, i, cnt = this._areas.length;
      for (i = 0; i < cnt; i++) {
          e = this._areas[i];
          if (e == area) {
              i &= ~1;
              e = this._areas[i];
              e.select(e);
              this._selectedArea = e;
              i++;
              e = this._areas[i];
              e.select(e);
          } else {
              e.select(null);
          }
      }
      return this._selectedArea != null ? this : null;
  };
  TableLayout.prototype.onMouseMove = function (x, y) {
      if (this._focusedRowIndex >= 0) {
          var upper = this._areas[this._focusedRowIndex];
          var lower = this._areas[this._focusedRowIndex + 2];
          var d = y - this._oldY;
          if (d == 0)
              return this;
          var upperBottom = this._oldUpperBottom + d;
          var lowerTop = this._oldLowerTop + d;
          if (upperBottom - upper.getTop() >= 60 && lower.getBottom() - lowerTop >= 60) {
              upper.setBottom(upperBottom);
              lower.setTop(lowerTop);
          }
          return this;
      }
      var i, cnt = this._areas.length - 2;
      for (i = 0; i < cnt; i += 2) {
          var b = this._areas[i].getBottom();
          if (y >= b - 4 && y < b + 4) {
              ChartManager.getInstance().showCursor('n-resize');
              return this;
          }
      }
      return null;
  };
  TableLayout.prototype.onMouseLeave = function (x, y) {
      this._focusedRowIndex = -1;
  };
  TableLayout.prototype.onMouseDown = function (x, y) {
      var i, cnt = this._areas.length - 2;
      for (i = 0; i < cnt; i += 2) {
          var b = this._areas[i].getBottom();
          if (y >= b - 4 && y < b + 4) {
              this._focusedRowIndex = i;
              this._oldY = y;
              this._oldUpperBottom = b;
              this._oldLowerTop = this._areas[i + 2].getTop();
              return this;
          }
      }
      return null;
  };
  TableLayout.prototype.onMouseUp = function (x, y) {
      if (this._focusedRowIndex >= 0) {
          this._focusedRowIndex = -1;
          var i, cnt = this._areas.length;
          var height = [];
          for (i = 0; i < cnt; i += 2) {
              height.push(this._areas[i].getHeight());
          }
          ChartSettings.get().charts.areaHeight = height;
          ChartSettings.save();
      }
      return this;
  };
  var DockableLayout = create_class(ChartAreaGroup);
  DockableLayout.prototype.__construct = function (name) {
      DockableLayout.__super.__construct.call(this, name);
  };
  DockableLayout.prototype.measure = function (context, width, height) {
      DockableLayout.__super.measure.call(this, context, width, height);
      width = this.getMeasuredWidth();
      height = this.getMeasuredHeight();
      for (var i in this._areas) {
          var area = this._areas[i];
          area.measure(context, width, height);
          switch (area.getDockStyle()) {
              case ChartArea.DockStyle.left:
              case ChartArea.DockStyle.Right:
                  width -= area.getMeasuredWidth();
                  break;
              case ChartArea.DockStyle.Top:
              case ChartArea.DockStyle.Bottom:
                  height -= area.getMeasuredHeight();
                  break;
              case ChartArea.DockStyle.Fill:
                  width = 0;
                  height = 0;
                  break;
          }
      }
  };
  DockableLayout.prototype.layout = function (left, top, right, bottom, forceChange) {
      DockableLayout.__super.layout.call(this, left, top, right, bottom, forceChange);
      left = this.getLeft();
      top = this.getTop();
      right = this.getRight();
      bottom = this.getBottom();
      var w, h;
      if (!forceChange)
          forceChange = this.isChanged();
      for (var i in this._areas) {
          var area = this._areas[i];
          switch (area.getDockStyle()) {
              case ChartArea.DockStyle.left:
                  w = area.getMeasuredWidth();
                  area.layout(left, top, left + w, bottom, forceChange);
                  left += w;
                  break;
              case ChartArea.DockStyle.Top:
                  h = area.getMeasuredHeight();
                  area.layout(left, top, right, top + h, forceChange);
                  top += h;
                  break;
              case ChartArea.DockStyle.Right:
                  w = area.getMeasuredWidth();
                  area.layout(right - w, top, right, bottom, forceChange);
                  right -= w;
                  break;
              case ChartArea.DockStyle.Bottom:
                  h = area.getMeasuredHeight();
                  area.layout(left, bottom - h, right, bottom, forceChange);
                  bottom -= h;
                  break;
              case ChartArea.DockStyle.Fill:
                  area.layout(left, top, right, bottom, forceChange);
                  left = right;
                  top = bottom;
                  break;
          }
      }
      this.setChanged(false);
  };
  DockableLayout.prototype.drawGrid = function (context) {
      var mgr = ChartManager.getInstance();
      var theme = mgr.getTheme(this.getFrameName());
      var left = this.getLeft();
      var top = this.getTop();
      var right = this.getRight();
      var bottom = this.getBottom();
      context.fillStyle = theme.getColor(this._gridColor);
      for (var i in this._areas) {
          var area = this._areas[i];
          switch (area.getDockStyle()) {
              case ChartArea.DockStyle.Left:
                  context.fillRect(area.getRight(), top, 1, bottom - top);
                  left += area.getWidth();
                  break;
              case ChartArea.DockStyle.Top:
                  context.fillRect(left, area.getBottom(), right - left, 1);
                  top += area.getHeight();
                  break;
              case ChartArea.DockStyle.Right:
                  context.fillRect(area.getLeft(), top, 1, bottom - top);
                  right -= area.getWidth();
                  break;
              case ChartArea.DockStyle.Bottom:
                  context.fillRect(left, area.getTop(), right - left, 1);
                  bottom -= area.getHeight();
                  break;
          }
      }
  };
  var ChartManager = create_class();
  ChartManager.DrawingTool = {
      Cursor: 0,
      CrossCursor: 1,
      DrawLines: 2,
      DrawFibRetrace: 3,
      DrawFibFans: 4,
      SegLine: 5,
      StraightLine: 6,
      ArrowLine: 7,
      RayLine: 8,
      HoriStraightLine: 9,
      HoriRayLine: 10,
      HoriSegLine: 11,
      VertiStraightLine: 12,
      PriceLine: 13,
      BiParallelLine: 14,
      BiParallelRayLine: 15,
      TriParallelLine: 16,
      BandLine: 17
  };
  ChartManager._instance = null;
  ChartManager.getInstance = function () {
      if (ChartManager._instance == null)
          ChartManager._instance = new ChartManager();
      return ChartManager._instance;
  };
  ChartManager.prototype.__construct = function () {
      this._dataSources = {};
      this._dataSourceCache = {};
      this._dataProviders = {};
      this._frames = {};
      this._areas = {};
      this._timelines = {};
      this._ranges = {};
      this._plotters = {};
      this._themes = {};
      this._titles = {};
      this._frameMousePos = {};
      this._dsChartStyle = {};
      this._dragStarted = false;
      this._oldX = 0;
      this._fakeIndicators = {};
      this._captureMouseWheelDirectly = true;
      this._chart = {};
      this._chart.defaultFrame = new Chart();
      this._drawingTool = ChartManager.DrawingTool["CrossCursor"];
      this._beforeDrawingTool = this._drawingTool;
      this._language = "zh-cn";
      this._mainCanvas = null;
      this._overlayCanvas = null;
      this._mainContext = null;
      this._overlayContext = null;
  };
  /**
   * redraw.
   * @param layer
   *   "all", "main", "overlay"
   */
  ChartManager.prototype.redraw = function (layer, refresh) {
      if (layer == undefined || refresh)
          layer = "All";
      if (layer == "All" || layer == "MainCanvas") {
          if (refresh)
              this.getFrame("frame0").setChanged(true);
          this.layout(this._mainContext, "frame0",
              0, 0, this._mainCanvas.width, this._mainCanvas.height);
          this.drawMain("frame0", this._mainContext);
      }
      if (layer == "All" || layer == "OverlayCanvas") {
          this._overlayContext.clearRect(
              0, 0, this._overlayCanvas.width, this._overlayCanvas.height);
          this.drawOverlay("frame0", this._overlayContext);
      }
  };
  /**
   * bindCanvas.
   * @param layer
   *   "main", "overlay"
   */
  ChartManager.prototype.bindCanvas = function (layer, canvas) {
      if (layer == "main") {
          this._mainCanvas = canvas;
          this._mainContext = canvas.getContext("2d");
      } else if (layer == "overlay") {
          this._overlayCanvas = canvas;
          this._overlayContext = canvas.getContext("2d");
          if (this._captureMouseWheelDirectly)
              $(this._overlayCanvas).bind('mousewheel', mouseWheel);
      }
  };
  ChartManager.prototype.getCaptureMouseWheelDirectly = function () {
      return this._captureMouseWheelDirectly;
  };
  ChartManager.prototype.setCaptureMouseWheelDirectly = function (v) {
      this._captureMouseWheelDirectly = v;
      if (v)
          $(this._overlayCanvas).bind('mousewheel', mouseWheel);
      else
          $(this._overlayCanvas).unbind('mousewheel');
  };
  ChartManager.prototype.getChart = function (nouseParam) {
      return this._chart["defaultFrame"];
  };
  ChartManager.prototype.init = function () {
      delete this._ranges['frame0.k0.indic1'];
      delete this._ranges['frame0.k0.indic1Range'];
      delete this._areas['frame0.k0.indic1'];
      delete this._areas['frame0.k0.indic1Range'];
      DefaultTemplate.loadTemplate("frame0.k0", "");
      this.redraw('All', true);
  };
  ChartManager.prototype.setCurrentDrawingTool = function (paramTool) {
      this._drawingTool = ChartManager.DrawingTool[paramTool];
      this.setRunningMode(this._drawingTool);
  };
  /**
   * Property: Language.
   * @param lang:
   *     "en-us" "zh-cn" "zh-tw"
   */
  ChartManager.prototype.getLanguage = function () {
      return this._language;
  };
  ChartManager.prototype.setLanguage = function (lang) {
      this._language = lang;
  };
  /**
   * Property: ThemeName.
   * @param lang:
   *     "Dark" "Light"
   */
  ChartManager.prototype.setThemeName = function (frameName, themeName) {
      if (themeName == undefined)
          themeName = "Dark";
      var theme;
      switch (themeName) {
          case "Light":
              theme = new LightTheme();
              break;
          default:
              themeName = "Dark";
              theme = new DarkTheme();
              break;
      }
      this._themeName = themeName;
      this.setTheme(frameName, theme);
      this.getFrame(frameName).setChanged(true);
  };
  /**
   * Property: ChartStyle.
   * @param style:
   *     "CandleStick" "CandleStickHLC" "OHLC"
   */
  ChartManager.prototype.getChartStyle = function (dsName) {
      var chartStyle = this._dsChartStyle[dsName];
      if (chartStyle == undefined)
          return "CandleStick";
      return chartStyle;
  };
  ChartManager.prototype.setChartStyle = function (dsName, style) {
      if (this._dsChartStyle[dsName] == style)
          return;
      var areaName = dsName + ".main";
      var dpName = areaName + ".main";
      var plotterName = areaName + ".main";
      var dp, plotter;
      switch (style) {
          case "CandleStick":
          case "CandleStickHLC":
          case "OHLC":
              dp = this.getDataProvider(dpName);
              if (dp == undefined || !is_instance(dp, MainDataProvider)) {
                  dp = new MainDataProvider(dpName);
                  this.setDataProvider(dpName, dp);
                  dp.updateData();
              }
              this.setMainIndicator(dsName, ChartSettings.get().charts.mIndic);
              switch (style) {
                  case "CandleStick":
                      plotter = new CandlestickPlotter(plotterName);
                      break;
                  case "CandleStickHLC":
                      plotter = new CandlestickHLCPlotter(plotterName);
                      break;
                  case "OHLC":
                      plotter = new OHLCPlotter(plotterName);
                      break;
              }
              this.setPlotter(plotterName, plotter);
              plotter = new MinMaxPlotter(areaName + ".decoration");
              this.setPlotter(plotter.getName(), plotter);
              break;
          case "Line":
              dp = new IndicatorDataProvider(dpName);
              this.setDataProvider(dp.getName(), dp);
              dp.setIndicator(new HLCIndicator());
              this.removeMainIndicator(dsName);
              plotter = new IndicatorPlotter(plotterName);
              this.setPlotter(plotterName, plotter);
              this.removePlotter(areaName + ".decoration");
              break;
      }
      this.getArea(plotter.getAreaName()).setChanged(true);
      this._dsChartStyle[dsName] = style;
  };
  ChartManager.prototype.setNormalMode = function () {
      this._drawingTool = this._beforeDrawingTool;
      $(".chart_dropdown_data").removeClass("chart_dropdown-hover");
      $("#chart_toolpanel .chart_toolpanel_button").removeClass("selected");
      $("#chart_CrossCursor").parent().addClass("selected");
      if (this._drawingTool == ChartManager.DrawingTool.Cursor) {
          this.showCursor();
          $("#mode a").removeClass("selected");
          $("#chart_toolpanel .chart_toolpanel_button").removeClass("selected");
          $("#chart_Cursor").parent().addClass("selected");
      } else {
          this.hideCursor();
      }
  };
  ChartManager.prototype.setRunningMode = function (mode) {
      var pds = this.getDataSource("frame0.k0");
      var curr_o = pds.getCurrentToolObject();
      if (curr_o != null && curr_o.state != CToolObject.state.AfterDraw) {
          pds.delToolObject();
      }
      if (pds.getToolObjectCount() > 10) {
          this.setNormalMode();
          return;
      }
      this._drawingTool = mode;
      if (mode == ChartManager.DrawingTool.Cursor) {
          this.showCursor();
      } else {
      }
      switch (mode) {
          case ChartManager.DrawingTool.Cursor: {
              this._beforeDrawingTool = mode;
              break;
          }
          case ChartManager.DrawingTool.ArrowLine: {
              pds.addToolObject(new CArrowLineObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.BandLine: {
              pds.addToolObject(new CBandLineObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.BiParallelLine: {
              pds.addToolObject(new CBiParallelLineObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.BiParallelRayLine: {
              pds.addToolObject(new CBiParallelRayLineObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.CrossCursor: {
              this._beforeDrawingTool = mode;
              break;
          }
          case ChartManager.DrawingTool.DrawFibFans: {
              pds.addToolObject(new CFibFansObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.DrawFibRetrace: {
              pds.addToolObject(new CFibRetraceObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.DrawLines: {
              pds.addToolObject(new CStraightLineObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.HoriRayLine: {
              pds.addToolObject(new CHoriRayLineObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.HoriSegLine: {
              pds.addToolObject(new CHoriSegLineObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.HoriStraightLine: {
              pds.addToolObject(new CHoriStraightLineObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.PriceLine: {
              pds.addToolObject(new CPriceLineObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.RayLine: {
              pds.addToolObject(new CRayLineObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.SegLine: {
              pds.addToolObject(new CSegLineObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.StraightLine: {
              pds.addToolObject(new CStraightLineObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.TriParallelLine: {
              pds.addToolObject(new CTriParallelLineObject("frame0.k0"));
              break;
          }
          case ChartManager.DrawingTool.VertiStraightLine: {
              pds.addToolObject(new CVertiStraightLineObject("frame0.k0"));
              break;
          }
      }
  };
  ChartManager.prototype.getTitle = function (dsName) {
      return this._titles[dsName];
  };
  ChartManager.prototype.setTitle = function (dsName, title) {
      this._titles[dsName] = title;
  };
  ChartManager.prototype.setCurrentDataSource = function (dsName, dsAlias) {
      var cached = this.getCachedDataSource(dsAlias);
      if (cached != null) {
          this.setDataSource(dsName, cached, true);
      } else {
          var ds = this.getDataSource(dsName);
          if (ds != null) {
              if (is_instance(ds, MainDataSource)) {
                  cached = new MainDataSource(dsAlias);
              } else if (is_instance(ds, CLiveOrderDataSource)) {
                  cached = new CLiveOrderDataSource(dsAlias);
              } else if (is_instance(ds, CLiveTradeDataSource)) {
                  cached = new CLiveTradeDataSource(dsAlias);
              }
              this.setDataSource(dsName, cached, true);
              this.setCachedDataSource(dsAlias, cached);
          }
      }
  };
  ChartManager.prototype.getDataSource = function (name) {
      return this._dataSources[name];
  };
  ChartManager.prototype.setDataSource = function (name, ds, forceRefresh) {
      this._dataSources[name] = ds;
      if (forceRefresh)
          this.updateData(name, null);
  };
  ChartManager.prototype.getCachedDataSource = function (name) {
      return this._dataSourceCache[name];
  };
  ChartManager.prototype.setCachedDataSource = function (name, ds) {
      this._dataSourceCache[name] = ds;
  };
  ChartManager.prototype.getDataProvider = function (name) {
      return this._dataProviders[name];
  };
  ChartManager.prototype.setDataProvider = function (name, dp) {
      this._dataProviders[name] = dp;
  };
  ChartManager.prototype.removeDataProvider = function (name) {
      delete this._dataProviders[name];
  };
  ChartManager.prototype.getFrame = function (name) {
      return this._frames[name];
  };
  ChartManager.prototype.setFrame = function (name, frame) {
      this._frames[name] = frame;
  };
  ChartManager.prototype.removeFrame = function (name) {
      delete this._frames[name];
  };
  ChartManager.prototype.getArea = function (name) {
      return this._areas[name];
  };
  ChartManager.prototype.setArea = function (name, area) {
      this._areas[name] = area;
  };
  ChartManager.prototype.removeArea = function (name) {
      delete this._areas[name];
  };
  ChartManager.prototype.getTimeline = function (name) {
      return this._timelines[name];
  };
  ChartManager.prototype.setTimeline = function (name, timeline) {
      this._timelines[name] = timeline;
  };
  ChartManager.prototype.removeTimeline = function (name) {
      delete this._timelines[name];
  };
  ChartManager.prototype.getRange = function (name) {
      return this._ranges[name];
  };
  ChartManager.prototype.setRange = function (name, range) {
      this._ranges[name] = range;
  };
  ChartManager.prototype.removeRange = function (name) {
      delete this._ranges[name];
  };
  ChartManager.prototype.getPlotter = function (name) {
      return this._plotters[name];
  };
  ChartManager.prototype.setPlotter = function (name, plotter) {
      this._plotters[name] = plotter;
  };
  ChartManager.prototype.removePlotter = function (name) {
      delete this._plotters[name];
  };
  ChartManager.prototype.getTheme = function (name) {
      return this._themes[name];
  };
  ChartManager.prototype.setTheme = function (name, theme) {
      this._themes[name] = theme;
  };
  ChartManager.prototype.getFrameMousePos = function (name, point) {
      if (this._frameMousePos[name] != undefined) {
          point.x = this._frameMousePos[name].x;
          point.y = this._frameMousePos[name].y;
      } else {
          point.x = -1;
          point.y = -1;
      }
  };
  ChartManager.prototype.setFrameMousePos = function (name, px, py) {
      this._frameMousePos[name] = {x: px, y: py};
  };
  ChartManager.prototype.drawArea = function (context, area, plotterNames) {
      var areaName = area.getNameObject().getCompAt(2);
      if (areaName == "timeline") {
          if (area.getHeight() < 20)
              return;
      } else {
          if (area.getHeight() < 30)
              return;
      }
      if (area.getWidth() < 30)
          return;
      areaName = area.getName();
      var plotter;
      var i, cnt = plotterNames.length;
      for (i = 0; i < cnt; i++) {
          plotter = this._plotters[areaName + plotterNames[i]];
          if (plotter != undefined)
              plotter.Draw(context);
      }
  };
  ChartManager.prototype.drawAreaMain = function (context, area) {
      var ds = this._dataSources[area.getDataSourceName()];
      var plotterNames;
      if (ds.getDataCount() < 1)
          plotterNames = [".background"];
      else
          plotterNames = [".background", ".grid", ".main", ".secondary"];
      this.drawArea(context, area, plotterNames);
      area.setChanged(false);
  };
  ChartManager.prototype.drawAreaOverlay = function (context, area) {
      var ds = this._dataSources[area.getDataSourceName()];
      var plotterNames;
      if (ds.getDataCount() < 1)
          plotterNames = [".selection"];
      else
          plotterNames = [".decoration", ".selection", ".info", ".tool"];
      this.drawArea(context, area, plotterNames);
  };
  ChartManager.prototype.drawMain = function (frameName, context) {
      var drawn = false;
  
      if (!drawn) {
          for (var it in this._areas) {
              if (this._areas[it].getFrameName() == frameName && !is_instance(this._areas[it], ChartAreaGroup))
                  this.drawAreaMain(context, this._areas[it]);
          }
      }
      var e;
      for (var i in this._timelines) {
          e = this._timelines[i];
          if (e.getFrameName() == frameName)
              e.setUpdated(false);
      }
      for (var i in this._ranges) {
          e = this._ranges[i];
          if (e.getFrameName() == frameName)
              e.setUpdated(false);
      }
      for (var i in this._areas) {
          e = this._areas[i];
          if (e.getFrameName() == frameName)
              e.setChanged(false);
      }
  };
  ChartManager.prototype.drawOverlay = function (frameName, context) {
      for (var n in this._areas) {
          var area = this._areas[n];
          if (is_instance(area, ChartAreaGroup))
              if (area.getFrameName() == frameName)
                  area.drawGrid(context);
      }
      for (var n in this._areas) {
          var area = this._areas[n];
          if (is_instance(area, ChartAreaGroup) == false)
              if (area.getFrameName() == frameName)
                  this.drawAreaOverlay(context, area);
      }
  };
  ChartManager.prototype.updateData = function (dsName, data) {
      var ds = this.getDataSource(dsName);
      if (ds == undefined)
          return;
      if (data != null) {
          if (!ds.update(data))
              return false;
          if (ds.getUpdateMode() == DataSource.UpdateMode.DoNothing)
              return true;
      } else {
          ds.setUpdateMode(DataSource.UpdateMode.Refresh);
      }
      var timeline = this.getTimeline(dsName);
      if (timeline != undefined)
          timeline.update();
      if (ds.getDataCount() < 1)
          return true;
      var dpNames = [".main", ".secondary"];
      var area, areaName;
      for (var n in this._areas) {
          area = this._areas[n];
          if (is_instance(area, ChartAreaGroup))
              continue;
          if (area.getDataSourceName() != dsName)
              continue;
          areaName = area.getName();
          for (var i = 0; i < dpNames.length; i++) {
              var dp = this.getDataProvider(areaName + dpNames[i]);
              if (dp != undefined)
                  dp.updateData();
          }
      }
      return true;
  };
  ChartManager.prototype.updateRange = function (dsName) {
      var ds = this.getDataSource(dsName);
      if (ds.getDataCount() < 1)
          return;
      var dpNames = [".main", ".secondary"];
      var area, areaName;
      for (var n in this._areas) {
          area = this._areas[n];
          if (is_instance(area, ChartAreaGroup))
              continue;
          if (area.getDataSourceName() != dsName)
              continue;
          areaName = area.getName();
          for (var i = 0; i < dpNames.length; i++) {
              var dp = this.getDataProvider(areaName + dpNames[i]);
              if (dp != undefined)
                  dp.updateRange();
          }
          var timeline = this.getTimeline(dsName);
          if (timeline != undefined && timeline.getMaxItemCount() > 0) {
              var range = this.getRange(areaName);
              if (range != undefined)
                  range.update();
          }
      }
  };
  ChartManager.prototype.layout = function (context, frameName, left, top, right, bottom) {
      var frame = this.getFrame(frameName);
      frame.measure(context, right - left, bottom - top);
      frame.layout(left, top, right, bottom);
      for (var n in this._timelines) {
          var e = this._timelines[n];
          if (e.getFrameName() == frameName)
              e.onLayout();
      }
      for (var n in this._dataSources) {
          if (n.substring(0, frameName.length) == frameName)
              this.updateRange(n);
      }
  };
  ChartManager.prototype.SelectRange = function (pArea, y) {
      var it;
      for (var ee in this._ranges) {
          var _1 = this._ranges[ee].getAreaName();
          var _2 = pArea.getName();
          if (_1 == _2)
              this._ranges[ee].selectAt(y);
          else
              this._ranges[ee].unselect();
      }
  };
  ChartManager.prototype.scale = function (s) {
      if (this._highlightedFrame == null)
          return;
      var hiArea = this._highlightedFrame.getHighlightedArea();
      if (this.getRange(hiArea.getName()) != undefined) {
          var dsName = hiArea.getDataSourceName();
          var timeline = this.getTimeline(dsName);
          if (timeline != null) {
              timeline.scale(s);
              this.updateRange(dsName);
          }
      }
  };
  ChartManager.prototype.showCursor = function (cursor) {
      if (cursor === undefined)
          cursor = 'default';
      this._mainCanvas.style.cursor = cursor;
      this._overlayCanvas.style.cursor = cursor;
  };
  ChartManager.prototype.hideCursor = function () {
      this._mainCanvas.style.cursor = 'none';
      this._overlayCanvas.style.cursor = 'none';
  };
  ChartManager.prototype.showCrossCursor = function (area, x, y) {
      var e = this.getRange(area.getName());
      if (e != undefined) {
          e.selectAt(y);
          e = this.getTimeline(area.getDataSourceName());
          if (e != undefined)
              if (e.selectAt(x))
                  return true;
      }
      return false;
  };
  ChartManager.prototype.hideCrossCursor = function (exceptTimeline) {
      if (exceptTimeline != null) {
          for (var n in this._timelines) {
              var e = this._timelines[n];
              if (e != exceptTimeline)
                  e.unselect();
          }
      } else {
          for (var n in this._timelines)
              this._timelines[n].unselect();
      }
      for (var n in this._ranges)
          this._ranges[n].unselect();
  };
  ChartManager.prototype.clearHighlight = function () {
      if (this._highlightedFrame != null) {
          this._highlightedFrame.highlight(null);
          this._highlightedFrame = null;
      }
  };
  ChartManager.prototype.onToolMouseMove = function (frameName, x, y) {
      var ret = false;
      frameName += ".";
      for (var n in this._dataSources) {
          if (n.indexOf(frameName) == 0) {
              var ds = this._dataSources[n];
              if (is_instance(ds, MainDataSource))
                  if (ds.toolManager.acceptMouseMoveEvent(x, y))
                      ret = true;
          }
      }
      return ret;
  };
  ChartManager.prototype.onToolMouseDown = function (frameName, x, y) {
      var ret = false;
      frameName += ".";
      for (var n in this._dataSources) {
          if (n.indexOf(frameName) == 0) {
              var ds = this._dataSources[n];
              if (is_instance(ds, MainDataSource))
                  if (ds.toolManager.acceptMouseDownEvent(x, y))
                      ret = true;
          }
      }
      return ret;
  };
  ChartManager.prototype.onToolMouseUp = function (frameName, x, y) {
      var ret = false;
      frameName += ".";
      for (var n in this._dataSources) {
          if (n.indexOf(frameName) == 0) {
              var ds = this._dataSources[n];
              if (is_instance(ds, MainDataSource))
                  if (ds.toolManager.acceptMouseUpEvent(x, y))
                      ret = true;
          }
      }
      return ret;
  };
  ChartManager.prototype.onToolMouseDrag = function (frameName, x, y) {
      var ret = false;
      frameName += ".";
      for (var n in this._dataSources) {
          if (n.indexOf(frameName) == 0) {
              var ds = this._dataSources[n];
              if (is_instance(ds, MainDataSource))
                  if (ds.toolManager.acceptMouseDownMoveEvent(x, y))
                      ret = true;
          }
      }
      return ret;
  };
  ChartManager.prototype.onMouseMove = function (frameName, x, y, drag) {
      var frame = this.getFrame(frameName);
      if (frame === undefined)
          return;
      this.setFrameMousePos(frameName, x, y);
      this.hideCrossCursor();
      if (this._highlightedFrame != frame)
          this.clearHighlight();
      if (this._capturingMouseArea != null) {
          this._capturingMouseArea.onMouseMove(x, y);
          return;
      }
      var areas = frame.contains(x, y);
      if (areas == null)
          return;
      var a, i, cnt = areas.length;
      for (i = cnt - 1; i >= 0; i--) {
          a = areas[i];
          a = a.onMouseMove(x, y);
          if (a != null) {
              if (!is_instance(a, ChartAreaGroup)) {
                  frame.highlight(a);
                  this._highlightedFrame = frame;
              }
              return;
          }
      }
  };
  ChartManager.prototype.onMouseLeave = function (frameName, x, y, move) {
      var frame = this.getFrame(frameName);
      if (frame == undefined)
          return;
      this.setFrameMousePos(frameName, x, y);
      this.hideCrossCursor();
      this.clearHighlight();
      if (this._capturingMouseArea != null) {
          this._capturingMouseArea.onMouseLeave(x, y);
          this._capturingMouseArea = null;
      }
      this._dragStarted = false;
  };
  ChartManager.prototype.onMouseDown = function (frameName, x, y) {
      var frame = this.getFrame(frameName);
      if (frame == undefined)
          return;
      var areas = frame.contains(x, y);
      if (areas == null)
          return;
      var a, i, cnt = areas.length;
      for (i = cnt - 1; i >= 0; i--) {
          a = areas[i];
          a = a.onMouseDown(x, y);
          if (a != null) {
              this._capturingMouseArea = a;
              return;
          }
      }
  };
  ChartManager.prototype.onMouseUp = function (frameName, x, y) {
      var frame = this.getFrame(frameName);
      if (frame == undefined)
          return;
      if (this._capturingMouseArea) {
          if (this._capturingMouseArea.onMouseUp(x, y) == null && this._dragStarted == false) {
              if (this._selectedFrame != null && this._selectedFrame != frame)
                  this._selectedFrame.select(null);
              if (this._capturingMouseArea.isSelected()) {
                  if (!this._captureMouseWheelDirectly)
                      $(this._overlayCanvas).unbind('mousewheel');
                  frame.select(null);
                  this._selectedFrame = null;
              } else {
                  if (this._selectedFrame != frame)
                      if (!this._captureMouseWheelDirectly)
                          $(this._overlayCanvas).bind('mousewheel', mouseWheel);
                  frame.select(this._capturingMouseArea);
                  this._selectedFrame = frame;
              }
          }
          this._capturingMouseArea = null;
          this._dragStarted = false;
      }
  };
  ChartManager.prototype.deleteToolObject = function () {
      var pDPTool = this.getDataSource("frame0.k0");
      var selectObject = pDPTool.getSelectToolObjcet();
      if (selectObject != null)
          pDPTool.delSelectToolObject();
      var currentObject = pDPTool.getCurrentToolObject();
      if (currentObject != null && currentObject.getState() != CToolObject.state.AfterDraw) {
          pDPTool.delToolObject();
      }
      this.setNormalMode();
  };
  ChartManager.prototype.unloadTemplate = function (frameName) {
      var frame = this.getFrame(frameName);
      if (frame == undefined)
          return;
      for (var n in this._dataSources) {
          if (n.match(frameName + "."))
              delete this._dataSources[n];
      }
      for (var n in this._dataProviders) {
          if (this._dataProviders[n].getFrameName() == frameName)
              delete this._dataProviders[n];
      }
      delete this._frames[frameName];
      for (var n in this._areas) {
          if (this._areas[n].getFrameName() == frameName)
              delete this._areas[n];
      }
      for (var n in this._timelines) {
          if (this._timelines[n].getFrameName() == frameName)
              delete this._timelines[n];
      }
      for (var n in this._ranges) {
          if (this._ranges[n].getFrameName() == frameName)
              delete this._ranges[n];
      }
      for (var n in this._plotters) {
          if (this._plotters[n].getFrameName() == frameName)
              delete this._plotters[n];
      }
      delete this._themes[frameName];
      delete this._frameMousePos[frameName];
  };
  ChartManager.prototype.createIndicatorAndRange = function (areaName, indicName, notLoadSettings) {
      var indic, range;
      switch (indicName) {
          case "MA":
              indic = new MAIndicator();
              range = new PositiveRange(areaName);
              break;
          case "EMA":
              indic = new EMAIndicator();
              range = new PositiveRange(areaName);
              break;
          case "VOLUME":
              indic = new VOLUMEIndicator();
              range = new ZeroBasedPositiveRange(areaName);
              break;
          case "MACD":
              indic = new MACDIndicator();
              range = new ZeroCenteredRange(areaName);
              break;
          case "DMI":
              indic = new DMIIndicator();
              range = new PercentageRange(areaName);
              break;
          case "DMA":
              indic = new DMAIndicator();
              range = new Range(areaName);
              break;
          case "TRIX":
              indic = new TRIXIndicator();
              range = new Range(areaName);
              break;
          case "BRAR":
              indic = new BRARIndicator();
              range = new Range(areaName);
              break;
          case "VR":
              indic = new VRIndicator();
              range = new Range(areaName);
              break;
          case "OBV":
              indic = new OBVIndicator();
              range = new Range(areaName);
              break;
          case "EMV":
              indic = new EMVIndicator();
              range = new Range(areaName);
              break;
          case "RSI":
              indic = new RSIIndicator();
              range = new PercentageRange(areaName);
              break;
          case "WR":
              indic = new WRIndicator();
              range = new PercentageRange(areaName);
              break;
          case "SAR":
              indic = new SARIndicator();
              range = new PositiveRange(areaName);
              break;
          case "KDJ":
              indic = new KDJIndicator();
              range = new PercentageRange(areaName);
              break;
          case "ROC":
              indic = new ROCIndicator();
              range = new Range(areaName);
              break;
          case "MTM":
              indic = new MTMIndicator();
              range = new Range(areaName);
              break;
          case "BOLL":
              indic = new BOLLIndicator();
              range = new Range(areaName);
              break;
          case "PSY":
              indic = new PSYIndicator();
              range = new Range(areaName);
              break;
          case "StochRSI":
              indic = new STOCHRSIIndicator();
              range = new PercentageRange(areaName);
              break;
          default:
              return null;
      }
      if (!notLoadSettings)
          indic.setParameters(ChartSettings.get().indics[indicName]);
      return {"indic": indic, "range": range};
  };
  ChartManager.prototype.setMainIndicator = function (dsName, indicName) {
      var areaName = dsName + ".main";
      var dp = this.getDataProvider(areaName + ".main");
      if (dp == undefined || !is_instance(dp, MainDataProvider))
          return false;
      var indic;
      switch (indicName) {
          case "MA":
              indic = new MAIndicator();
              break;
          case "EMA":
              indic = new EMAIndicator();
              break;
          case "BOLL":
              indic = new BOLLIndicator();
              break;
          case "SAR":
              indic = new SARIndicator();
              break;
          default:
              return false;
      }
      indic.setParameters(ChartSettings.get().indics[indicName]);
      var indicDpName = areaName + ".secondary";
      var indicDp = this.getDataProvider(indicDpName);
      if (indicDp == undefined) {
          indicDp = new IndicatorDataProvider(indicDpName);
          this.setDataProvider(indicDp.getName(), indicDp);
      }
      indicDp.setIndicator(indic);
      var plotter = this.getPlotter(indicDpName);
      if (plotter == undefined) {
          plotter = new IndicatorPlotter(indicDpName);
          this.setPlotter(plotter.getName(), plotter);
      }
      this.getArea(areaName).setChanged(true);
      return true;
  };
  ChartManager.prototype.setIndicator = function (areaName, indicName) {
      var area = this.getArea(areaName);
      if (area == undefined || area.getNameObject().getCompAt(2) == "main")
          return false;
      var dp = this.getDataProvider(areaName + ".secondary");
      if (dp == undefined || !is_instance(dp, IndicatorDataProvider))
          return false;
      var ret = this.createIndicatorAndRange(areaName, indicName);
      if (ret == null)
          return false;
      var indic = ret.indic;
      var range = ret.range;
      this.removeDataProvider(areaName + ".main");
      this.removePlotter(areaName + ".main");
      this.removeRange(areaName);
      this.removePlotter(areaName + "Range.decoration");
      dp.setIndicator(indic);
      this.setRange(areaName, range);
      range.setPaddingTop(20);
      range.setPaddingBottom(4);
      range.setMinInterval(20);
      if (is_instance(indic, VOLUMEIndicator)) {
          var plotter = new LastVolumePlotter(areaName + "Range.decoration");
          this.setPlotter(plotter.getName(), plotter);
      } else if (is_instance(indic, BOLLIndicator) || is_instance(indic, SARIndicator)) {
          var dp = new MainDataProvider(areaName + ".main");
          this.setDataProvider(dp.getName(), dp);
          dp.updateData();
          var plotter = new OHLCPlotter(areaName + ".main");
          this.setPlotter(plotter.getName(), plotter);
      }
      return true;
  };
  ChartManager.prototype.removeMainIndicator = function (dsName) {
      var areaName = dsName + ".main";
      var indicDpName = areaName + ".secondary";
      var indicDp = this.getDataProvider(indicDpName);
      if (indicDp == undefined || !is_instance(indicDp, IndicatorDataProvider))
          return;
      this.removeDataProvider(indicDpName);
      this.removePlotter(indicDpName);
      this.getArea(areaName).setChanged(true);
  };
  ChartManager.prototype.removeIndicator = function (areaName) {
      var area = this.getArea(areaName);
      if (area == undefined || area.getNameObject().getCompAt(2) == "main")
          return;
      var dp = this.getDataProvider(areaName + ".secondary");
      if (dp == undefined || !is_instance(dp, IndicatorDataProvider))
          return;
      var rangeAreaName = areaName + "Range";
      var rangeArea = this.getArea(rangeAreaName);
      if (rangeArea == undefined)
          return;
      var tableLayout = this.getArea(area.getDataSourceName() + ".charts");
      if (tableLayout == undefined)
          return;
      tableLayout.removeArea(area);
      this.removeArea(areaName);
      tableLayout.removeArea(rangeArea);
      this.removeArea(rangeAreaName);
      for (var n in this._dataProviders) {
          if (this._dataProviders[n].getAreaName() == areaName)
              this.removeDataProvider(n);
      }
      for (var n in this._ranges) {
          if (this._ranges[n].getAreaName() == areaName)
              this.removeRange(n);
      }
      for (var n in this._plotters) {
          if (this._plotters[n].getAreaName() == areaName)
              this.removePlotter(n);
      }
      for (var n in this._plotters) {
          if (this._plotters[n].getAreaName() == rangeAreaName)
              this.removePlotter(n);
      }
  };
  /**
   * getIndicatorParameters
   *     获取指标的ParamExpr数组
   * @param indicName
   * @returns {*}
   */
  ChartManager.prototype.getIndicatorParameters = function (indicName) {
      var indic = this._fakeIndicators[indicName];
      if (indic == undefined) {
          var ret = this.createIndicatorAndRange("", indicName);
          if (ret == null)
              return null;
          this._fakeIndicators[indicName] = indic = ret.indic;
      }
      var params = [];
      var i, cnt = indic.getParameterCount();
      for (i = 0; i < cnt; i++)
          params.push(indic.getParameterAt(i));
      return params;
  };
  ChartManager.prototype.setIndicatorParameters = function (indicName, params) {
      var n, indic;
      for (n in this._dataProviders) {
          var dp = this._dataProviders[n];
          if (is_instance(dp, IndicatorDataProvider) == false)
              continue;
          indic = dp.getIndicator();
          if (indic.getName() == indicName) {
              indic.setParameters(params);
              dp.refresh();
              this.getArea(dp.getAreaName()).setChanged(true);
          }
      }
      indic = this._fakeIndicators[indicName];
      if (indic == undefined) {
          var ret = this.createIndicatorAndRange("", indicName, true);
          if (ret == null)
              return;
          this._fakeIndicators[indicName] = indic = ret.indic;
      }
      indic.setParameters(params);
  };
  ChartManager.prototype.getIndicatorAreaName = function (dsName, index) {
      var tableLayout = this.getArea(dsName + ".charts");
      var cnt = tableLayout.getAreaCount() >> 1;
      if (index < 0 || index >= cnt)
          return "";
      return tableLayout.getAreaAt(index << 1).getName();
  };
  var Timeline = create_class(NamedObject);
  Timeline._ItemWidth = [1, 3, 3, 5, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29];
  Timeline._SpaceWidth = [1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 7, 7, 7];
  Timeline.PADDING_LEFT = 4;
  Timeline.PADDING_RIGHT = 8;
  Timeline.prototype.__construct = function (name) {
      Timeline.__super.__construct.call(this, name);
      this._updated = false;
      this._innerLeft = 0;
      this._innerWidth = 0;
      this._firstColumnLeft = 0;
      this._scale = 3;
      this._lastScale = -1;
      this._maxItemCount = 0;
      this._maxIndex = 0;
      this._firstIndex = -1;
      this._selectedIndex = -1;
      this._savedFirstIndex = -1;
  };
  Timeline.prototype.isLatestShown = function () {
      return this.getLastIndex() == this._maxIndex;
  };
  Timeline.prototype.isUpdated = function () {
      return this._updated;
  };
  Timeline.prototype.setUpdated = function (v) {
      this._updated = v;
  };
  Timeline.prototype.getItemWidth = function () {
      return Timeline._ItemWidth[this._scale];
  };
  Timeline.prototype.getSpaceWidth = function () {
      return Timeline._SpaceWidth[this._scale];
  };
  Timeline.prototype.getColumnWidth = function () {
      return this.getSpaceWidth() + this.getItemWidth();
  };
  Timeline.prototype.getInnerWidth = function () {
      return this._innerWidth;
  };
  Timeline.prototype.getItemLeftOffset = function () {
      return this.getSpaceWidth();
  };
  Timeline.prototype.getItemCenterOffset = function () {
      return this.getSpaceWidth() + (this.getItemWidth() >> 1);
  };
  Timeline.prototype.getFirstColumnLeft = function () {
      return this._firstColumnLeft;
  };
  Timeline.prototype.getMaxItemCount = function () {
      return this._maxItemCount;
  };
  Timeline.prototype.getFirstIndex = function () {
      return this._firstIndex;
  };
  Timeline.prototype.getLastIndex = function () {
      return Math.min(this._firstIndex + this._maxItemCount, this._maxIndex);
  };
  Timeline.prototype.getSelectedIndex = function () {
      return this._selectedIndex;
  };
  Timeline.prototype.getMaxIndex = function () {
      return this._maxIndex;
  };
  Timeline.prototype.calcColumnCount = function (w) {
      return Math.floor(w / this.getColumnWidth()) << 0;
  };
  Timeline.prototype.calcFirstColumnLeft = function (maxItemCount) {
      return this._innerLeft + this._innerWidth - (this.getColumnWidth() * maxItemCount);
  };
  Timeline.prototype.calcFirstIndexAlignRight = function (oldFirstIndex, oldMaxItemCount, newMaxItemCount) {
      return Math.max(0, oldFirstIndex + Math.max(oldMaxItemCount, 1) - Math.max(newMaxItemCount, 1));
  };
  Timeline.prototype.calcFirstIndex = function (newMaxItemCount) {
      return this.validateFirstIndex(
          this.calcFirstIndexAlignRight(this._firstIndex, this._maxItemCount,
              newMaxItemCount), newMaxItemCount);
  };
  Timeline.prototype.updateMaxItemCount = function () {
      var newMaxItemCount = this.calcColumnCount(this._innerWidth);
      var newFirstIndex;
      if (this._maxItemCount < 1) {
          newFirstIndex = this.calcFirstIndex(newMaxItemCount);
      } else if (this._lastScale == this._scale) {
          newFirstIndex = this.validateFirstIndex(this._firstIndex - (newMaxItemCount - this._maxItemCount));
      } else {
          var focusedIndex = (this._selectedIndex >= 0) ? this._selectedIndex : this.getLastIndex() - 1;
          newFirstIndex = this.validateFirstIndex(focusedIndex -
              Math.round((focusedIndex - this._firstIndex) * newMaxItemCount / this._maxItemCount));
      }
      this._lastScale = this._scale;
      if (this._firstIndex != newFirstIndex) {
          if (this._selectedIndex == this._firstIndex)
              this._selectedIndex = newFirstIndex;
          this._firstIndex = newFirstIndex;
          this._updated = true;
      }
      if (this._maxItemCount != newMaxItemCount) {
          this._maxItemCount = newMaxItemCount;
          this._updated = true;
      }
      this._firstColumnLeft = this.calcFirstColumnLeft(newMaxItemCount);
  };
  Timeline.prototype.validateFirstIndex = function (firstIndex, maxItemCount) {
      if (this._maxIndex < 1)
          return -1;
      if (firstIndex < 0)
          return 0;
      var lastFirst = Math.max(0, this._maxIndex - 1 /*maxItemCount*/);
      if (firstIndex > lastFirst)
          return lastFirst;
      return firstIndex;
  };
  Timeline.prototype.validateSelectedIndex = function () {
      if (this._selectedIndex < this._firstIndex)
          this._selectedIndex = -1;
      else if (this._selectedIndex >= this.getLastIndex())
          this._selectedIndex = -1;
  };
  Timeline.prototype.onLayout = function () {
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getDataSourceName() + ".main");
      if (area != null) {
          this._innerLeft = area.getLeft() + Timeline.PADDING_LEFT;
          var w = Math.max(0, area.getWidth() - (Timeline.PADDING_LEFT + Timeline.PADDING_RIGHT));
          if (this._innerWidth != w) {
              this._innerWidth = w;
              this.updateMaxItemCount();
          }
      }
  };
  Timeline.prototype.toIndex = function (x) {
      return this._firstIndex + this.calcColumnCount(x - this._firstColumnLeft);
  };
  Timeline.prototype.toColumnLeft = function (index) {
      return this._firstColumnLeft + (this.getColumnWidth() * (index - this._firstIndex));
  };
  Timeline.prototype.toItemLeft = function (index) {
      return this.toColumnLeft(index) + this.getItemLeftOffset();
  };
  Timeline.prototype.toItemCenter = function (index) {
      return this.toColumnLeft(index) + this.getItemCenterOffset();
  };
  Timeline.prototype.selectAt = function (x) {
      this._selectedIndex = this.toIndex(x);
      this.validateSelectedIndex();
      return (this._selectedIndex >= 0);
  };
  Timeline.prototype.unselect = function () {
      this._selectedIndex = -1;
  };
  Timeline.prototype.update = function () {
      var mgr = ChartManager.getInstance();
      var ds = mgr.getDataSource(this.getDataSourceName());
      var oldMaxIndex = this._maxIndex;
      this._maxIndex = ds.getDataCount();
      switch (ds.getUpdateMode()) {
          case DataSource.UpdateMode.Refresh:
              if (this._maxIndex < 1)
                  this._firstIndex = -1;
              else
                  this._firstIndex = Math.max(this._maxIndex - this._maxItemCount, 0);
              this._selectedIndex = -1;
              this._updated = true;
              break;
          case DataSource.UpdateMode.Append:
              var lastIndex = this.getLastIndex();
              var erasedCount = ds.getErasedCount();
              if (lastIndex < oldMaxIndex) {
                  if (erasedCount > 0) {
                      this._firstIndex = Math.max(this._firstIndex - erasedCount, 0);
                      if (this._selectedIndex >= 0) {
                          this._selectedIndex -= erasedCount;
                          this.validateSelectedIndex();
                      }
                      this._updated = true;
                  }
              } else if (lastIndex == oldMaxIndex) {
                  this._firstIndex += (this._maxIndex - oldMaxIndex);
                  if (this._selectedIndex >= 0) {
                      this._selectedIndex -= erasedCount;
                      this.validateSelectedIndex();
                  }
                  this._updated = true;
              }
              break;
      }
  };
  Timeline.prototype.move = function (x) {
      if (this.isLatestShown())
          ChartManager.getInstance().getArea(
              this.getDataSourceName() + ".mainRange").setChanged(true);
      this._firstIndex = this.validateFirstIndex(
          this._savedFirstIndex - this.calcColumnCount(x), this._maxItemCount);
      this._updated = true;
      if (this._selectedIndex >= 0)
          this.validateSelectedIndex();
  };
  Timeline.prototype.startMove = function () {
      this._savedFirstIndex = this._firstIndex;
  };
  Timeline.prototype.scale = function (s) {
      this._scale += s;
      if (this._scale < 0)
          this._scale = 0;
      else if (this._scale >= Timeline._ItemWidth.length)
          this._scale = Timeline._ItemWidth.length - 1;
      this.updateMaxItemCount();
      if (this._selectedIndex >= 0)
          this.validateSelectedIndex();
  };
  var Range = create_class(NamedObject);
  Range.prototype.__construct = function (name) {
      Range.__super.__construct.call(this, name);
      this._updated = true;
      this._minValue = Number.MAX_VALUE;
      this._maxValue = -Number.MAX_VALUE;
      this._outerMinValue = Number.MAX_VALUE;
      this._outerMaxValue = -Number.MAX_VALUE;
      this._ratio = 0;
      this._top = 0;
      this._bottom = 0;
      this._paddingTop = 0;
      this._paddingBottom = 0;
      this._minInterval = 36;
      this._selectedPosition = -1;
      this._selectedValue = -Number.MAX_VALUE;
      this._gradations = [];
  };
  Range.prototype.isUpdated = function () {
      return this._updated;
  };
  Range.prototype.setUpdated = function (v) {
      this._updated = v;
  };
  Range.prototype.getMinValue = function () {
      return this._minValue;
  };
  Range.prototype.getMaxValue = function () {
      return this._maxValue;
  };
  Range.prototype.getRange = function () {
      return this._maxValue - this._minValue;
  };
  Range.prototype.getOuterMinValue = function () {
      return this._outerMinValue;
  };
  Range.prototype.getOuterMaxValue = function () {
      return this._outerMaxValue;
  };
  Range.prototype.getOuterRange = function () {
      return this._outerMaxValue - this._outerMinValue;
  };
  Range.prototype.getHeight = function () {
      return Math.max(0, this._bottom - this._top);
  };
  Range.prototype.getGradations = function () {
      return this._gradations;
  };
  Range.prototype.getMinInterval = function () {
      return this._minInterval;
  };
  Range.prototype.setMinInterval = function (v) {
      this._minInterval = v;
  };
  Range.prototype.getSelectedPosition = function () {
      if (this._selectedPosition >= 0)
          return this._selectedPosition;
      if (this._selectedValue > -Number.MAX_VALUE)
          return this.toY(this._selectedValue);
      return -1;
  };
  Range.prototype.getSelectedValue = function () {
      if (this._selectedValue > -Number.MAX_VALUE)
          return this._selectedValue;
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getAreaName());
      if (area == null)
          return -Number.MAX_VALUE;
      if (this._selectedPosition < area.getTop() + 12 || this._selectedPosition >= area.getBottom() - 4)
          return -Number.MAX_VALUE;
      return this.toValue(this._selectedPosition);
  };
  Range.prototype.setPaddingTop = function (p) {
      this._paddingTop = p;
  };
  Range.prototype.setPaddingBottom = function (p) {
      this._paddingBottom = p;
  };
  Range.prototype.toValue = function (y) {
      return this._maxValue - (y - this._top) / this._ratio;
  };
  Range.prototype.toY = function (value) {
      if (this._ratio > 0)
          return this._top + Math.floor((this._maxValue - value) * this._ratio + 0.5);
      return this._top;
  };
  Range.prototype.toHeight = function (value) {
      return Math.floor(value * this._ratio + 1.5);
  };
  Range.prototype.update = function () {
      var min = Number.MAX_VALUE;
      var max = -Number.MAX_VALUE;
      var mgr = ChartManager.getInstance();
      var dp, dpNames = [".main", ".secondary"];
      for (var i = 0; i < dpNames.length; i++) {
          dp = mgr.getDataProvider(this.getName() + dpNames[i]);
          if (dp != null) {
              min = Math.min(min, dp.getMinValue());
              max = Math.max(max, dp.getMaxValue());
          }
      }
      var r = {"min": min, "max": max};
      this.preSetRange(r);
      this.setRange(r.min, r.max);
  };
  Range.prototype.select = function (v) {
      this._selectedValue = v;
      this._selectedPosition = -1;
  };
  Range.prototype.selectAt = function (y) {
      this._selectedPosition = y;
      this._selectedValue = -Number.MAX_VALUE;
  };
  Range.prototype.unselect = function () {
      this._selectedPosition = -1;
      this._selectedValue = -Number.MAX_VALUE;
  };
  Range.prototype.preSetRange = function (r) {
      if (r.min == r.max) {
          r.min = -1.0;
          r.max = 1.0;
      }
  };
  Range.prototype.setRange = function (minValue, maxValue) {
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getAreaName());
      if (this._minValue == minValue && this._maxValue == maxValue && !area.isChanged())
          return;
      this._updated = true;
      this._minValue = minValue;
      this._maxValue = maxValue;
      this._gradations = [];
      var top = area.getTop() + this._paddingTop;
      var bottom = area.getBottom() - (this._paddingBottom + 1);
      if (top >= bottom) {
          this._minValue = this._maxValue;
          return;
      }
      this._top = top;
      this._bottom = bottom;
      if (this._maxValue > this._minValue)
          this._ratio = (bottom - top) / (this._maxValue - this._minValue);
      else {
          this._ratio = 1;
      }
      this._outerMinValue = this.toValue(area.getBottom());
      this._outerMaxValue = this.toValue(area.getTop());
      this.updateGradations();
  };
  Range.prototype.calcInterval = function () {
      var H = this.getHeight();
      var h = this.getMinInterval();
      if ((H / h) <= 1)
          h = H >> 1;
      var d = this.getRange();
      var i = 0;
      while (i > -2 && Math.floor(d) < d) {
          d *= 10.0;
          i--;
      }
      var m, c;
      for (; ; i++) {
          c = Math.pow(10.0, i);
          m = c;
          if (this.toHeight(m) > h)
              break;
          m = 2.0 * c;
          if (this.toHeight(m) > h)
              break;
          m = 5.0 * c;
          if (this.toHeight(m) > h)
              break;
      }
      return m;
  };
  Range.prototype.updateGradations = function () {
      this._gradations = [];
      var interval = this.calcInterval();
      if (interval <= 0.0)
          return;
      var v = Math.floor(this.getMaxValue() / interval) * interval;
      do {
          this._gradations.push(v);
          v -= interval;
      } while (v > this.getMinValue());
  };
  var PositiveRange = create_class(Range);
  PositiveRange.prototype.__construct = function (name) {
      PositiveRange.__super.__construct.call(this, name);
  };
  PositiveRange.prototype.preSetRange = function (r) {
      if (r.min < 0) r.min = 0;
      if (r.max < 0) r.max = 0;
  };
  var ZeroBasedPositiveRange = create_class(Range);
  ZeroBasedPositiveRange.prototype.__construct = function (name) {
      ZeroBasedPositiveRange.__super.__construct.call(this, name);
  };
  ZeroBasedPositiveRange.prototype.preSetRange = function (r) {
      r.min = 0;
      if (r.max < 0) r.max = 0;
  };
  var MainRange = create_class(Range);
  MainRange.prototype.__construct = function (name) {
      MainRange.__super.__construct.call(this, name);
  };
  MainRange.prototype.preSetRange = function (r) {
      var mgr = ChartManager.getInstance();
  
      var timeline = mgr.getTimeline(this.getDataSourceName());
      var dIndex = timeline.getMaxIndex() - timeline.getLastIndex();
      if (dIndex < 25) {
          var ds = mgr.getDataSource(this.getDataSourceName());
  
          var data = ds.getDataAt(ds.getDataCount() - 1);
          var d = ((r.max - r.min) / 4) * (1 - (dIndex / 25));
  
          r.min = Math.min(r.min, Math.max(data.low - d, 0));
          r.max = Math.max(r.max, data.high + d);
      }
  
      if (r.min > 0) {
          var a = r.max / r.min;
  
          if (a < 1.016) {
              var m = (r.max + r.min) / 2.0;
              var c = (a - 1.0) * 1.5;
              r.max = m * (1.0 + c);
              r.min = m * (1.0 - c);
          } else if (a < 1.048) {
              var m = (r.max + r.min) / 2.0;
              r.max = m * 1.024;
              r.min = m * 0.976;
          }
      }
      if (r.min < 0) r.min = 0;
      if (r.max < 0) r.max = 0;
  };
  
  var ZeroCenteredRange = create_class(Range);
  ZeroCenteredRange.prototype.__construct = function (name) {
      ZeroCenteredRange.__super.__construct.call(this, name);
  };
  ZeroCenteredRange.prototype.calcInterval = function (area) {
      var h = this.getMinInterval();
      if (area.getHeight() / h < 2)
          return 0.0;
      var r = this.getRange();
      var i;
      for (i = 3; ; i += 2) {
          if (this.toHeight(r / i) <= h)
              break;
      }
      i -= 2;
      return r / i;
  };
  ZeroCenteredRange.prototype.updateGradations = function () {
      this._gradations = [];
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getAreaName());
      var interval = this.calcInterval(area);
      if (interval <= 0.0)
          return;
      var v = interval / 2.0;
      do {
          this._gradations.push(v);
          this._gradations.push(-v);
          v += interval;
      } while (v <= this.getMaxValue());
  };
  ZeroCenteredRange.prototype.preSetRange = function (r) {
      var abs = Math.max(Math.abs(r.min), Math.abs(r.max));
      r.min = -abs;
      r.max = abs;
  };
  var PercentageRange = create_class(Range);
  PercentageRange.prototype.__construct = function (name) {
      PercentageRange.__super.__construct.call(this, name);
  };
  PercentageRange.prototype.updateGradations = function () {
      this._gradations = [];
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getAreaName());
      var interval = 10.0;
      var h = Math.floor(this.toHeight(interval));
      if ((h << 2) > area.getHeight())
          return;
      var v = Math.ceil(this.getMinValue() / interval) * interval;
      if (v == 0.0)
          v = 0;
      if ((h << 2) < 24) {
          if ((h << 1) < 8)
              return;
          do {
              if (v == 20.0 || v == 80.0)
                  this._gradations.push(v);
              v += interval;
          } while (v < this.getMaxValue());
      } else {
          do {
              if (h < 8) {
                  if (v == 20.0 || v == 50.0 || v == 80.0)
                      this._gradations.push(v);
              } else {
                  if (v == 0.0 || v == 20.0 || v == 50.0 || v == 80.0 || v == 100.0)
                      this._gradations.push(v);
              }
              v += interval;
          } while (v < this.getMaxValue());
      }
  };
  var DataSource = create_class(NamedObject);
  DataSource.prototype.__construct = function (name) {
      DataSource.__super.__construct.call(this, name);
  };
  DataSource.UpdateMode = {
      DoNothing: 0,
      Refresh: 1,
      Update: 2,
      Append: 3
  };
  DataSource.prototype.getUpdateMode = function () {
      return this._updateMode;
  };
  DataSource.prototype.setUpdateMode = function (mode) {
      this._updateMode = mode;
  };
  DataSource.prototype.getCacheSize = function () {
      return 0;
  };
  DataSource.prototype.getDataCount = function () {
      return 0;
  };
  var MainDataSource = create_class(DataSource);
  MainDataSource.prototype.__construct = function (name) {
      MainDataSource.__super.__construct.call(this, name);
      this._erasedCount = 0;
      this._dataItems = [];
      this._decimalDigits = 0;
      this.toolManager = new CToolManager(name);
  };
  MainDataSource.prototype.getCacheSize = function () {
      return this._dataItems.length;
  };
  MainDataSource.prototype.getDataCount = function () {
      return this._dataItems.length;
  };
  MainDataSource.prototype.getUpdatedCount = function () {
      return this._updatedCount;
  };
  MainDataSource.prototype.getAppendedCount = function () {
      return this._appendedCount;
  };
  MainDataSource.prototype.getErasedCount = function () {
      return this._erasedCount;
  };
  MainDataSource.prototype.getDecimalDigits = function () {
      return this._decimalDigits;
  };
  MainDataSource.prototype.calcDecimalDigits = function (v) {
      var str = "" + v;
      var i = str.indexOf('.');
      if (i < 0)
          return 0;
      return (str.length - 1) - i;
  };
  MainDataSource.prototype.getLastDate = function () {
  
      var count = this.getDataCount();
      if (count < 1)
          return -1;
      return this.getDataAt(count - 1).date;
  };
  MainDataSource.prototype.getDataAt = function (index) {
      return this._dataItems[index];
  };
  MainDataSource.prototype.update = function (data) {
  
      this._updatedCount = 0;
      this._appendedCount = 0;
      this._erasedCount = 0;
      var len = this._dataItems.length;
      if (len > 0) {
          var lastIndex = len - 1;
          var lastItem = this._dataItems[lastIndex];
          var e, i, cnt = data.length;
          for (i = 0; i < cnt; i++) {
              e = data[i];
              if (e[0] == lastItem.date) {
                  if (lastItem.open == e[1] &&
                      lastItem.high == e[2] &&
                      lastItem.low == e[3] &&
                      lastItem.close == e[4] &&
                      lastItem.volume == e[5]) {
                      this.setUpdateMode(DataSource.UpdateMode.DoNothing);
                  } else {
                      this.setUpdateMode(DataSource.UpdateMode.Update);
                      this._dataItems[lastIndex] = {
                          date: e[0],
                          open: e[1],
                          high: e[2],
                          low: e[3],
                          close: e[4],
                          volume: e[5]
                      };
                      this._updatedCount++;
                  }
                  i++;
                  if (i < cnt) {
                      this.setUpdateMode(DataSource.UpdateMode.Append);
                      for (; i < cnt; i++, this._appendedCount++) {
                          e = data[i];
                          this._dataItems.push({
                              date: e[0],
                              open: e[1],
                              high: e[2],
                              low: e[3],
                              close: e[4],
                              volume: e[5]
                          });
                      }
                  }
                  return true;
              }
          }
          if (cnt < KlineIns.limit) {
              this.setUpdateMode(DataSource.UpdateMode.DoNothing);
              return false;
          }
      }
      this.setUpdateMode(DataSource.UpdateMode.Refresh);
      this._dataItems = [];
      var d, n, e, i, cnt = data.length;
      for (i = 0; i < cnt; i++) {
          e = data[i];
          for (n = 1; n <= 4; n++) {
              d = this.calcDecimalDigits(e[n]);
              if (this._decimalDigits < d)
                  this._decimalDigits = d;
          }
          this._dataItems.push({
              date: e[0],
              open: e[1],
              high: e[2],
              low: e[3],
              close: e[4],
              volume: e[5]
          });
      }
      return true;
  };
  MainDataSource.prototype.select = function (id) {
      this.toolManager.selecedObject = id;
  };
  MainDataSource.prototype.unselect = function () {
      this.toolManager.selecedObject = -1;
  };
  MainDataSource.prototype.addToolObject = function (toolObject) {
      this.toolManager.addToolObject(toolObject);
  };
  MainDataSource.prototype.delToolObject = function () {
      this.toolManager.delCurrentObject();
  };
  MainDataSource.prototype.getToolObject = function (index) {
      return this.toolManager.getToolObject(index);
  };
  MainDataSource.prototype.getToolObjectCount = function () {
      return this.toolManager.toolObjects.length;
  };
  MainDataSource.prototype.getCurrentToolObject = function () {
      return this.toolManager.getCurrentObject();
  };
  MainDataSource.prototype.getSelectToolObjcet = function () {
      return this.toolManager.getSelectedObject();
  };
  MainDataSource.prototype.delSelectToolObject = function () {
      this.toolManager.delSelectedObject();
  };
  var DataProvider = create_class(NamedObject);
  DataProvider.prototype.__construct = function (name) {
      DataProvider.__super.__construct.call(this, name);
      this._minValue = 0;
      this._maxValue = 0;
      this._minValueIndex = -1;
      this._maxValueIndex = -1;
  };
  DataProvider.prototype.getMinValue = function () {
      return this._minValue;
  };
  DataProvider.prototype.getMaxValue = function () {
      return this._maxValue;
  };
  DataProvider.prototype.getMinValueIndex = function () {
      return this._minValueIndex;
  };
  DataProvider.prototype.getMaxValueIndex = function () {
      return this._maxValueIndex;
  };
  DataProvider.prototype.calcRange = function (firstIndexes, lastIndex, minmaxes, indexes) {
      var min = Number.MAX_VALUE;
      var max = -Number.MAX_VALUE;
      var minIndex = -1;
      var maxIndex = -1;
      var minmax = {};
      var i = lastIndex - 1;
      var n = firstIndexes.length - 1;
      for (; n >= 0; n--) {
          var first = firstIndexes[n];
          if (i < first) {
              minmaxes[n] = {"min": min, "max": max};
          } else {
              for (; i >= first; i--) {
                  if (this.getMinMaxAt(i, minmax) == false)
                      continue;
                  if (min > minmax.min) {
                      min = minmax.min;
                      minIndex = i;
                  }
                  if (max < minmax.max) {
                      max = minmax.max;
                      maxIndex = i;
                  }
              }
              minmaxes[n] = {"min": min, "max": max};
          }
          if (indexes != null) {
              indexes[n] = {"minIndex": minIndex, "maxIndex": maxIndex};
          }
      }
  };
  DataProvider.prototype.updateRange = function () {
      var mgr = ChartManager.getInstance();
      var timeline = mgr.getTimeline(this.getDataSourceName());
      var firstIndexes = [timeline.getFirstIndex()];
      var minmaxes = [{}];
      var indexes = [{}];
      this.calcRange(firstIndexes, timeline.getLastIndex(), minmaxes, indexes);
      this._minValue = minmaxes[0].min;
      this._maxValue = minmaxes[0].max;
      this._minValueIndex = indexes[0].minIndex;
      this._maxValueIndex = indexes[0].maxIndex;
  };
  var MainDataProvider = create_class(DataProvider);
  MainDataProvider.prototype.__construct = function (name) {
      MainDataProvider.__super.__construct.call(this, name);
      this._candlestickDS = null;
  };
  MainDataProvider.prototype.updateData = function () {
      var mgr = ChartManager.getInstance();
      var ds = mgr.getDataSource(this.getDataSourceName());
      if (!is_instance(ds, MainDataSource))
          return;
      this._candlestickDS = ds;
  };
  MainDataProvider.prototype.getMinMaxAt = function (index, minmax) {
      var data = this._candlestickDS.getDataAt(index);
      minmax.min = data.low;
      minmax.max = data.high;
      return true;
  };
  var IndicatorDataProvider = create_class(DataProvider);
  IndicatorDataProvider.prototype.getIndicator = function () {
      return this._indicator;
  };
  IndicatorDataProvider.prototype.setIndicator = function (v) {
      this._indicator = v;
      this.refresh();
  };
  IndicatorDataProvider.prototype.refresh = function () {
      var mgr = ChartManager.getInstance();
      var ds = mgr.getDataSource(this.getDataSourceName());
      if (ds.getDataCount() < 1)
          return;
      var indic = this._indicator;
      var i, last = ds.getDataCount();
      indic.clear();
      indic.reserve(last);
      for (i = 0; i < last; i++)
          indic.execute(ds, i);
  };
  IndicatorDataProvider.prototype.updateData = function () {
      var mgr = ChartManager.getInstance();
      var ds = mgr.getDataSource(this.getDataSourceName());
      if (ds.getDataCount() < 1)
          return;
      var indic = this._indicator;
      var mode = ds.getUpdateMode();
      switch (mode) {
          case DataSource.UpdateMode.Refresh: {
              this.refresh();
              break;
          }
          case DataSource.UpdateMode.Append: {
              indic.reserve(ds.getAppendedCount());
          }
          case DataSource.UpdateMode.Update: {
              var i, last = ds.getDataCount();
              var cnt = ds.getUpdatedCount() + ds.getAppendedCount();
              for (i = last - cnt; i < last; i++)
                  indic.execute(ds, i);
              break;
          }
      }
  };
  IndicatorDataProvider.prototype.getMinMaxAt = function (index, minmax) {
      minmax.min = Number.MAX_VALUE;
      minmax.max = -Number.MAX_VALUE;
      var result, valid = false;
      var i, cnt = this._indicator.getOutputCount();
      for (i = 0; i < cnt; i++) {
          result = this._indicator.getOutputAt(i).execute(index);
          if (isNaN(result) == false) {
              valid = true;
              if (minmax.min > result)
                  minmax.min = result;
              if (minmax.max < result)
                  minmax.max = result;
          }
      }
      return valid;
  };
  var theme_color_id = 0;
  var theme_font_id = 0;
  var Theme = create_class();
  Theme.prototype.getColor = function (which) {
      return this._colors[which];
  };
  Theme.prototype.getFont = function (which) {
      return this._fonts[which];
  };
  Theme.Color = {
      Positive: theme_color_id++,
      Negative: theme_color_id++,
      PositiveDark: theme_color_id++,
      NegativeDark: theme_color_id++,
      Unchanged: theme_color_id++,
      Background: theme_color_id++,
      Cursor: theme_color_id++,
      RangeMark: theme_color_id++,
      Indicator0: theme_color_id++,
      Indicator1: theme_color_id++,
      Indicator2: theme_color_id++,
      Indicator3: theme_color_id++,
      Indicator4: theme_color_id++,
      Indicator5: theme_color_id++,
      Grid0: theme_color_id++,
      Grid1: theme_color_id++,
      Grid2: theme_color_id++,
      Grid3: theme_color_id++,
      Grid4: theme_color_id++,
      TextPositive: theme_color_id++,
      TextNegative: theme_color_id++,
      Text0: theme_color_id++,
      Text1: theme_color_id++,
      Text2: theme_color_id++,
      Text3: theme_color_id++,
      Text4: theme_color_id++,
      LineColorNormal: theme_color_id++,
      LineColorSelected: theme_color_id++,
      CircleColorFill: theme_color_id++,
      CircleColorStroke: theme_color_id++
  };
  Theme.Font = {
      Default: theme_font_id++
  };
  var DarkTheme = create_class(Theme);
  DarkTheme.prototype.__construct = function () {
      this._colors = [];
      
      if (KlineIns.reverseColor) {
          this._colors[Theme.Color.Positive] = "#990e0e";
          this._colors[Theme.Color.Negative] = "#19b34c";
          this._colors[Theme.Color.PositiveDark] = "#3b0e08";
          this._colors[Theme.Color.NegativeDark] = "#004718";
      } else {
          this._colors[Theme.Color.Positive] = "#19b34c";
          this._colors[Theme.Color.Negative] = "#990e0e";
          this._colors[Theme.Color.PositiveDark] = "#004718";
          this._colors[Theme.Color.NegativeDark] = "#3b0e08";
      }
      this._colors[Theme.Color.Unchanged] = "#fff";
      this._colors[Theme.Color.Background] = "#1a243b";
      this._colors[Theme.Color.Cursor] = "#aaa";
      this._colors[Theme.Color.RangeMark] = "#f9ee30";
      this._colors[Theme.Color.Indicator0] = "#ddd";
      this._colors[Theme.Color.Indicator1] = "#f9ee30";
      this._colors[Theme.Color.Indicator2] = "#f600ff";
      this._colors[Theme.Color.Indicator3] = "#6bf";
      this._colors[Theme.Color.Indicator4] = "#a5cf81";
      this._colors[Theme.Color.Indicator5] = "#e18b89";
      this._colors[Theme.Color.Grid0] = "#333";
      this._colors[Theme.Color.Grid1] = "#444";
      this._colors[Theme.Color.Grid2] = "#666";
      this._colors[Theme.Color.Grid3] = "#888";
      this._colors[Theme.Color.Grid4] = "#aaa";
      this._colors[Theme.Color.TextPositive] = "#1bd357";
      this._colors[Theme.Color.TextNegative] = "#ff6f5e";
      this._colors[Theme.Color.Text0] = "#444";
      this._colors[Theme.Color.Text1] = "#666";
      this._colors[Theme.Color.Text2] = "#888";
      this._colors[Theme.Color.Text3] = "#aaa";
      this._colors[Theme.Color.Text4] = "#ccc";
      this._colors[Theme.Color.LineColorNormal] = "#a6a6a6";
      this._colors[Theme.Color.LineColorSelected] = "#ffffff";
      this._colors[Theme.Color.CircleColorFill] = "#000000";
      this._colors[Theme.Color.CircleColorStroke] = "#ffffff";
      this._fonts = [];
      this._fonts[Theme.Font.Default] = "12px Tahoma";
  };
  var LightTheme = create_class(Theme);
  LightTheme.prototype.__construct = function () {
      this._colors = [];
  
      if (KlineIns.reverseColor) {
          this._colors[Theme.Color.Positive] = "#db5542";
          this._colors[Theme.Color.Negative] = "#53b37b";
          this._colors[Theme.Color.PositiveDark] = "#ffadaa";
          this._colors[Theme.Color.NegativeDark] = "#66d293";
      } else {
          this._colors[Theme.Color.Positive] = "#53b37b";
          this._colors[Theme.Color.Negative] = "#db5542";
          this._colors[Theme.Color.PositiveDark] = "#66d293";
          this._colors[Theme.Color.NegativeDark] = "#ffadaa";
      }
      this._colors[Theme.Color.Unchanged] = "#fff";
      this._colors[Theme.Color.Background] = "#fff";
      this._colors[Theme.Color.Cursor] = "#aaa";
      this._colors[Theme.Color.RangeMark] = "#f27935";
      this._colors[Theme.Color.Indicator0] = "#2fd2b2";
      this._colors[Theme.Color.Indicator1] = "#ffb400";
      this._colors[Theme.Color.Indicator2] = "#e849b9";
      this._colors[Theme.Color.Indicator3] = "#1478c8";
      this._colors[Theme.Color.Grid0] = "#eee";
      this._colors[Theme.Color.Grid1] = "#afb1b3";
      this._colors[Theme.Color.Grid2] = "#ccc";
      this._colors[Theme.Color.Grid3] = "#bbb";
      this._colors[Theme.Color.Grid4] = "#aaa";
      this._colors[Theme.Color.TextPositive] = "#53b37b";
      this._colors[Theme.Color.TextNegative] = "#db5542";
      this._colors[Theme.Color.Text0] = "#ccc";
      this._colors[Theme.Color.Text1] = "#aaa";
      this._colors[Theme.Color.Text2] = "#888";
      this._colors[Theme.Color.Text3] = "#666";
      this._colors[Theme.Color.Text4] = "#444";
      this._colors[Theme.Color.LineColorNormal] = "#8c8c8c";
      this._colors[Theme.Color.LineColorSelected] = "#393c40";
      this._colors[Theme.Color.CircleColorFill] = "#ffffff";
      this._colors[Theme.Color.CircleColorStroke] = "#393c40";
      this._fonts = [];
      this._fonts[Theme.Font.Default] = "12px Tahoma";
  };
  var TemplateMeasuringHandler = create_class();
  TemplateMeasuringHandler.onMeasuring = function (sender, args) {
      var width = args.Width;
      var height = args.Height;
      var areaName = sender.getNameObject().getCompAt(2);
      if (areaName == "timeline")
          sender.setMeasuredDimension(width, 22);
  };
  var Template = create_class();
  Template.displayVolume = true;
  Template.createCandlestickDataSource = function (dsAlias) {
      return new MainDataSource(dsAlias);
  };
  Template.createLiveOrderDataSource = function (dsAlias) {
      return new CLiveOrderDataSource(dsAlias);
  };
  Template.createLiveTradeDataSource = function (dsAlias) {
      return new CLiveTradeDataSource(dsAlias);
  };
  Template.createDataSource = function (dsName, dsAlias, createFunc) {
      var mgr = ChartManager.getInstance();
      if (mgr.getCachedDataSource(dsAlias) == null)
          mgr.setCachedDataSource(dsAlias, createFunc(dsAlias));
      mgr.setCurrentDataSource(dsName, dsAlias);
      mgr.updateData(dsName, null);
  };
  Template.createTableComps = function (dsName) {
      Template.createMainChartComps(dsName);
      if (Template.displayVolume)
          Template.createIndicatorChartComps(dsName, "VOLUME");
      Template.createTimelineComps(dsName);
  };
  Template.createMainChartComps = function (dsName) {
      var mgr = ChartManager.getInstance();
      var tableLayout = mgr.getArea(dsName + ".charts");
      var areaName = dsName + ".main";
      var rangeAreaName = areaName + "Range";
      var area = new MainArea(areaName);
      mgr.setArea(areaName, area);
      tableLayout.addArea(area);
      var rangeArea = new MainRangeArea(rangeAreaName);
      mgr.setArea(rangeAreaName, rangeArea);
      tableLayout.addArea(rangeArea);
      var dp = new MainDataProvider(areaName + ".main");
      mgr.setDataProvider(dp.getName(), dp);
      mgr.setMainIndicator(dsName, "MA");
      var range = new MainRange(areaName);
      mgr.setRange(range.getName(), range);
      range.setPaddingTop(28);
      range.setPaddingBottom(12);
      var plotter = new MainAreaBackgroundPlotter(areaName + ".background");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new CGridPlotter(areaName + ".grid");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new CandlestickPlotter(areaName + ".main");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new MinMaxPlotter(areaName + ".decoration");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new MainInfoPlotter(areaName + ".info");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new SelectionPlotter(areaName + ".selection");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new CDynamicLinePlotter(areaName + ".tool");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new RangeAreaBackgroundPlotter(areaName + "Range.background");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new COrderGraphPlotter(areaName + "Range.grid");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new RangePlotter(areaName + "Range.main");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new RangeSelectionPlotter(areaName + "Range.selection");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new LastClosePlotter(areaName + "Range.decoration");
      mgr.setPlotter(plotter.getName(), plotter);
  };
  Template.createIndicatorChartComps = function (dsName, indicName) {
      var mgr = ChartManager.getInstance();
      var tableLayout = mgr.getArea(dsName + ".charts");
      var areaName = dsName + ".indic" + tableLayout.getNextRowId();
      var rangeAreaName = areaName + "Range";
      var area = new IndicatorArea(areaName);
      mgr.setArea(areaName, area);
      tableLayout.addArea(area);
      var rowIndex = tableLayout.getAreaCount() >> 1;
      var heights = ChartSettings.get().charts.areaHeight;
      if (heights.length > rowIndex) {
          var a, i;
          for (i = 0; i < rowIndex; i++) {
              a = tableLayout.getAreaAt(i << 1);
              a.setTop(0);
              a.setBottom(heights[i]);
          }
          area.setTop(0);
          area.setBottom(heights[rowIndex]);
      }
      var rangeArea = new IndicatorRangeArea(rangeAreaName);
      mgr.setArea(rangeAreaName, rangeArea);
      tableLayout.addArea(rangeArea);
      var dp = new IndicatorDataProvider(areaName + ".secondary");
      mgr.setDataProvider(dp.getName(), dp);
      if (mgr.setIndicator(areaName, indicName) == false) {
          mgr.removeIndicator(areaName);
          return;
      }
      var plotter = new MainAreaBackgroundPlotter(areaName + ".background");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new CGridPlotter(areaName + ".grid");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new IndicatorPlotter(areaName + ".secondary");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new IndicatorInfoPlotter(areaName + ".info");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new SelectionPlotter(areaName + ".selection");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new RangeAreaBackgroundPlotter(areaName + "Range.background");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new RangePlotter(areaName + "Range.main");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new RangeSelectionPlotter(areaName + "Range.selection");
      mgr.setPlotter(plotter.getName(), plotter);
  };
  Template.createTimelineComps = function (dsName) {
      var mgr = ChartManager.getInstance();
      var plotter;
      var timeline = new Timeline(dsName);
      mgr.setTimeline(timeline.getName(), timeline);
      plotter = new TimelineAreaBackgroundPlotter(dsName + ".timeline.background");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new TimelinePlotter(dsName + ".timeline.main");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new TimelineSelectionPlotter(dsName + ".timeline.selection");
      mgr.setPlotter(plotter.getName(), plotter);
  };
  Template.createLiveOrderComps = function (dsName) {
      var mgr = ChartManager.getInstance();
      var plotter;
      plotter = new BackgroundPlotter(dsName + ".main.background");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new CLiveOrderPlotter(dsName + ".main.main");
      mgr.setPlotter(plotter.getName(), plotter);
  };
  Template.createLiveTradeComps = function (dsName) {
      var mgr = ChartManager.getInstance();
      var plotter;
      plotter = new BackgroundPlotter(dsName + ".main.background");
      mgr.setPlotter(plotter.getName(), plotter);
      plotter = new CLiveTradePlotter(dsName + ".main.main");
      mgr.setPlotter(plotter.getName(), plotter);
  };
  var DefaultTemplate = create_class(Template);
  DefaultTemplate.loadTemplate = function (dsName, dsAlias) {
      var mgr = ChartManager.getInstance();
      var settings = ChartSettings.get();
      var frameName = (new CName(dsName)).getCompAt(0);
      mgr.unloadTemplate(frameName);
      Template.createDataSource(dsName, dsAlias, Template.createCandlestickDataSource);
      var frame = new DockableLayout(frameName);
      mgr.setFrame(frame.getName(), frame);
      mgr.setArea(frame.getName(), frame);
      frame.setGridColor(Theme.Color.Grid1);
      var area = new TimelineArea(dsName + ".timeline");
      mgr.setArea(area.getName(), area);
      frame.addArea(area);
      area.setDockStyle(ChartArea.DockStyle.Bottom);
      area.Measuring.addHandler(area, TemplateMeasuringHandler.onMeasuring);
      var tableLayout = new TableLayout(dsName + ".charts");
      mgr.setArea(tableLayout.getName(), tableLayout);
      tableLayout.setDockStyle(ChartArea.DockStyle.Fill);
      frame.addArea(tableLayout);
      Template.createTableComps(dsName);
      mgr.setThemeName(frameName, settings.theme);
      return mgr;
  };
  var Plotter = create_class(NamedObject);
  Plotter.prototype.__construct = function (name) {
      Plotter.__super.__construct.call(this, name);
  };
  Plotter.isChrome = (navigator.userAgent.toLowerCase().match(/chrome/) != null);
  Plotter.drawLine = function (context, x1, y1, x2, y2) {
      context.beginPath();
      context.moveTo((x1 << 0) + 0.5, (y1 << 0) + 0.5);
      context.lineTo((x2 << 0) + 0.5, (y2 << 0) + 0.5);
      context.stroke();
  };
  Plotter.drawLines = function (context, points) {
      var i, cnt = points.length;
      context.beginPath();
      context.moveTo(points[0].x, points[0].y);
      for (i = 1; i < cnt; i++)
          context.lineTo(points[i].x, points[i].y);
      if (Plotter.isChrome) {
          context.moveTo(points[0].x, points[0].y);
          for (i = 1; i < cnt; i++)
              context.lineTo(points[i].x, points[i].y);
      }
      context.stroke();
  };
  Plotter.drawDashedLine = function (context, x1, y1, x2, y2, dashLen, dashSolid) {
      if (dashLen < 2)
          dashLen = 2;
      var dX = x2 - x1;
      var dY = y2 - y1;
      context.beginPath();
      if (dY == 0) {
          var count = (dX / dashLen + 0.5) << 0;
          for (var i = 0; i < count; i++) {
              context.rect(x1, y1, dashSolid, 1);
              x1 += dashLen;
          }
          context.fill();
      } else {
          var count = (Math.sqrt(dX * dX + dY * dY) / dashLen + 0.5) << 0;
          dX = dX / count;
          dY = dY / count;
          var dashX = dX * dashSolid / dashLen;
          var dashY = dY * dashSolid / dashLen;
          for (var i = 0; i < count; i++) {
              context.moveTo(x1 + 0.5, y1 + 0.5);
              context.lineTo(x1 + 0.5 + dashX, y1 + 0.5 + dashY);
              x1 += dX;
              y1 += dY;
          }
          context.stroke();
      }
  };
  Plotter.createHorzDashedLine = function (context, x1, x2, y, dashLen, dashSolid) {
      if (dashLen < 2)
          dashLen = 2;
      var dX = x2 - x1;
      var count = (dX / dashLen + 0.5) << 0;
      for (var i = 0; i < count; i++) {
          context.rect(x1, y, dashSolid, 1);
          x1 += dashLen;
      }
  };
  Plotter.createRectangles = function (context, rects) {
      context.beginPath();
      var e, i, cnt = rects.length;
      for (i = 0; i < cnt; i++) {
          e = rects[i];
          context.rect(e.x, e.y, e.w, e.h);
      }
  };
  Plotter.createPolygon = function (context, points) {
      context.beginPath();
      context.moveTo(points[0].x + 0.5, points[0].y + 0.5);
      var i, cnt = points.length;
      for (i = 1; i < cnt; i++)
          context.lineTo(points[i].x + 0.5, points[i].y + 0.5);
      context.closePath();
  };
  Plotter.drawString = function (context, str, rect) {
      var w = context.measureText(str).width;
      if (rect.w < w)
          return false;
      context.fillText(str, rect.x, rect.y);
      rect.x += w;
      rect.w -= w;
      return true;
  };
  var BackgroundPlotter = create_class(Plotter);
  BackgroundPlotter.prototype.__construct = function (name) {
      BackgroundPlotter.__super.__construct.call(this, name);
      this._color = Theme.Color.Background;
  };
  BackgroundPlotter.prototype.getColor = function () {
      return this._color;
  };
  BackgroundPlotter.prototype.setColor = function (c) {
      this._color = c;
  };
  BackgroundPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getAreaName());
      var theme = mgr.getTheme(this.getFrameName());
      context.fillStyle = theme.getColor(this._color);
      context.fillRect(area.getLeft(), area.getTop(), area.getWidth(), area.getHeight());
  };
  var MainAreaBackgroundPlotter = create_class(BackgroundPlotter);
  MainAreaBackgroundPlotter.prototype.__construct = function (name) {
      MainAreaBackgroundPlotter.__super.__construct.call(this, name);
  };
  MainAreaBackgroundPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getAreaName());
      var timeline = mgr.getTimeline(this.getDataSourceName());
      var range = mgr.getRange(this.getAreaName());
      var theme = mgr.getTheme(this.getFrameName());
      var rect = area.getRect();
      if (!area.isChanged() && !timeline.isUpdated() && !range.isUpdated()) {
          var first = timeline.getFirstIndex();
          var last = timeline.getLastIndex() - 2;
          var start = Math.max(first, last);
          rect.X = timeline.toColumnLeft(start);
          rect.Width = area.getRight() - rect.X;
      }
      context.fillStyle = theme.getColor(this._color);
      context.fillRect(rect.X, rect.Y, rect.Width, rect.Height);
  };
  var RangeAreaBackgroundPlotter = create_class(BackgroundPlotter);
  RangeAreaBackgroundPlotter.prototype.__construct = function (name) {
      RangeAreaBackgroundPlotter.__super.__construct.call(this, name);
  };
  RangeAreaBackgroundPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var areaName = this.getAreaName();
      var area = mgr.getArea(areaName);
      var range = mgr.getRange(areaName.substring(0, areaName.lastIndexOf("Range")));
      var isMainRange = range.getNameObject().getCompAt(2) == "main";
      if (isMainRange) {
      } else {
          if (!area.isChanged() && !range.isUpdated())
              return;
      }
      var theme = mgr.getTheme(this.getFrameName());
      context.fillStyle = theme.getColor(this._color);
      context.fillRect(area.getLeft(), area.getTop(), area.getWidth(), area.getHeight());
  };
  var TimelineAreaBackgroundPlotter = create_class(BackgroundPlotter);
  TimelineAreaBackgroundPlotter.prototype.__construct = function (name) {
      TimelineAreaBackgroundPlotter.__super.__construct.call(this, name);
  };
  TimelineAreaBackgroundPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getAreaName());
      var timeline = mgr.getTimeline(this.getDataSourceName());
      if (!area.isChanged() && !timeline.isUpdated())
          return;
      var theme = mgr.getTheme(this.getFrameName());
      context.fillStyle = theme.getColor(this._color);
      context.fillRect(area.getLeft(), area.getTop(), area.getWidth(), area.getHeight());
  };
  var CGridPlotter = create_class(NamedObject);
  CGridPlotter.prototype.__construct = function (name) {
      CGridPlotter.__super.__construct.call(this, name);
  };
  CGridPlotter.prototype.Draw = function (context) {
      //return;//去掉背景价格虚线
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getAreaName());
      var timeline = mgr.getTimeline(this.getDataSourceName());
      var range = mgr.getRange(this.getAreaName());
      var clipped = false;
      if (!area.isChanged() && !timeline.isUpdated() && !range.isUpdated()) {
          var first = timeline.getFirstIndex();
          var last = timeline.getLastIndex();
          var start = Math.max(first, last - 2);
          var left = timeline.toColumnLeft(start);
          context.save();
          context.rect(left, area.getTop(), area.getRight() - left, area.getHeight());
          context.clip();
          clipped = true;
      }
      var theme = mgr.getTheme(this.getFrameName());
      context.fillStyle = theme.getColor(Theme.Color.Grid0);
      context.beginPath();
      var dashLen = 4,
          dashSolid = 1;
      if (Plotter.isChrome) {
          dashLen = 4;
          dashSolid = 1;
      }
      var gradations = range.getGradations();
      for (var n in gradations)
          Plotter.createHorzDashedLine(context,
              area.getLeft(), area.getRight(), range.toY(gradations[n]), dashLen, dashSolid);
      context.fill();
      if (clipped)
          context.restore();
  };
  var CandlestickPlotter = create_class(NamedObject);
  CandlestickPlotter.prototype.__construct = function (name) {
      CandlestickPlotter.__super.__construct.call(this, name);
  };
  CandlestickPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var ds = mgr.getDataSource(this.getDataSourceName());
      if (ds.getDataCount() < 1)
          return;
      var area = mgr.getArea(this.getAreaName());
      var timeline = mgr.getTimeline(this.getDataSourceName());
      var range = mgr.getRange(this.getAreaName());
      if (range.getRange() == 0.0)
          return;
      var theme = mgr.getTheme(this.getFrameName());
      var dark = is_instance(theme, DarkTheme);
      var first = timeline.getFirstIndex();
      var last = timeline.getLastIndex();
      var start;
      if (area.isChanged() || timeline.isUpdated() || range.isUpdated())
          start = first;
      else
          start = Math.max(first, last - 2);
      var cW = timeline.getColumnWidth();
      var iW = timeline.getItemWidth();
      var left = timeline.toItemLeft(start);
      var center = timeline.toItemCenter(start);
      var strokePosRects = [];
      var fillPosRects = [];
      var fillUchRects = [];
      var fillNegRects = [];
      for (var i = start; i < last; i++) {
          var data = ds.getDataAt(i);
          var high = range.toY(data.high);
          var low = range.toY(data.low);
          var open = data.open;
          var close = data.close;
          if (close > open) {
              var top = range.toY(close);
              var bottom = range.toY(open);
              var iH = Math.max(bottom - top, 1);
              if (iH > 1 && iW > 1 && dark)
                  strokePosRects.push({x: left + 0.5, y: top + 0.5, w: iW - 1, h: iH - 1});
              else
                  fillPosRects.push({x: left, y: top, w: Math.max(iW, 1), h: Math.max(iH, 1)});
              if (data.high > close) {
                  high = Math.min(high, top - 1);
                  fillPosRects.push({x: center, y: high, w: 1, h: top - high});
              }
              if (open > data.low) {
                  low = Math.max(low, bottom + 1);
                  fillPosRects.push({x: center, y: bottom, w: 1, h: low - bottom});
              }
          } else if (close == open) {
              var top = range.toY(close);
              fillUchRects.push({x: left, y: top, w: Math.max(iW, 1), h: 1});
              if (data.high > close)
                  high = Math.min(high, top - 1);
              if (open > data.low)
                  low = Math.max(low, top + 1);
              if (high < low)
                  fillUchRects.push({x: center, y: high, w: 1, h: low - high});
          } else {
              var top = range.toY(open);
              var bottom = range.toY(close);
              var iH = Math.max(bottom - top, 1);
              fillNegRects.push({x: left, y: top, w: Math.max(iW, 1), h: Math.max(iH, 1)});
              if (data.high > open)
                  high = Math.min(high, top - 1);
              if (close > data.low)
                  low = Math.max(low, bottom + 1);
              if (high < low)
                  fillNegRects.push({x: center, y: high, w: 1, h: low - high});
          }
          left += cW;
          center += cW;
      }
      if (strokePosRects.length > 0) {
          context.strokeStyle = theme.getColor(Theme.Color.Positive);
          Plotter.createRectangles(context, strokePosRects);
          context.stroke();
      }
      if (fillPosRects.length > 0) {
          context.fillStyle = theme.getColor(Theme.Color.Positive);
          Plotter.createRectangles(context, fillPosRects);
          context.fill();
      }
      if (fillUchRects.length > 0) {
          context.fillStyle = theme.getColor(Theme.Color.Negative);
          Plotter.createRectangles(context, fillUchRects);
          context.fill();
      }
      if (fillNegRects.length > 0) {
          context.fillStyle = theme.getColor(Theme.Color.Negative);
          Plotter.createRectangles(context, fillNegRects);
          context.fill();
      }
  };
  var CandlestickHLCPlotter = create_class(Plotter);
  CandlestickHLCPlotter.prototype.__construct = function (name) {
      CandlestickHLCPlotter.__super.__construct.call(this, name);
  };
  CandlestickHLCPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var ds = mgr.getDataSource(this.getDataSourceName());
      if (!is_instance(ds, MainDataSource) || ds.getDataCount() < 1)
          return;
      var area = mgr.getArea(this.getAreaName());
      var timeline = mgr.getTimeline(this.getDataSourceName());
      var range = mgr.getRange(this.getAreaName());
      if (range.getRange() == 0.0)
          return;
      var theme = mgr.getTheme(this.getFrameName());
      var dark = is_instance(theme, DarkTheme);
      var first = timeline.getFirstIndex();
      var last = timeline.getLastIndex();
      var start;
      if (area.isChanged() || timeline.isUpdated() || range.isUpdated())
          start = first;
      else
          start = Math.max(first, last - 2);
      var cW = timeline.getColumnWidth();
      var iW = timeline.getItemWidth();
      var left = timeline.toItemLeft(start);
      var center = timeline.toItemCenter(start);
      var strokePosRects = [];
      var fillPosRects = [];
      var fillUchRects = [];
      var fillNegRects = [];
      for (var i = start; i < last; i++) {
          var data = ds.getDataAt(i);
          var high = range.toY(data.high);
          var low = range.toY(data.low);
          var open = data.open;
          if (i > 0)
              open = ds.getDataAt(i - 1).close;
          var close = data.close;
          if (close > open) {
              var top = range.toY(close);
              var bottom = range.toY(open);
              var iH = Math.max(bottom - top, 1);
              if (iH > 1 && iW > 1 && dark)
                  strokePosRects.push({x: left + 0.5, y: top + 0.5, w: iW - 1, h: iH - 1});
              else
                  fillPosRects.push({x: left, y: top, w: Math.max(iW, 1), h: Math.max(iH, 1)});
              if (data.high > close) {
                  high = Math.min(high, top - 1);
                  fillPosRects.push({x: center, y: high, w: 1, h: top - high});
              }
              if (open > data.low) {
                  low = Math.max(low, bottom + 1);
                  fillPosRects.push({x: center, y: bottom, w: 1, h: low - bottom});
              }
          } else if (close == open) {
              var top = range.toY(close);
              fillUchRects.push({x: left, y: top, w: Math.max(iW, 1), h: 1});
              if (data.high > close)
                  high = Math.min(high, top - 1);
              if (open > data.low)
                  low = Math.max(low, top + 1);
              if (high < low)
                  fillUchRects.push({x: center, y: high, w: 1, h: low - high});
          } else {
              var top = range.toY(open);
              var bottom = range.toY(close);
              var iH = Math.max(bottom - top, 1);
              fillNegRects.push({x: left, y: top, w: Math.max(iW, 1), h: Math.max(iH, 1)});
              if (data.high > open)
                  high = Math.min(high, top - 1);
              if (close > data.low)
                  low = Math.max(low, bottom + 1);
              if (high < low)
                  fillNegRects.push({x: center, y: high, w: 1, h: low - high});
          }
          left += cW;
          center += cW;
      }
      if (strokePosRects.length > 0) {
          context.strokeStyle = theme.getColor(Theme.Color.Positive);
          Plotter.createRectangles(context, strokePosRects);
          context.stroke();
      }
      if (fillPosRects.length > 0) {
          context.fillStyle = theme.getColor(Theme.Color.Positive);
          Plotter.createRectangles(context, fillPosRects);
          context.fill();
      }
      if (fillUchRects.length > 0) {
          context.fillStyle = theme.getColor(Theme.Color.Negative);
          Plotter.createRectangles(context, fillUchRects);
          context.fill();
      }
      if (fillNegRects.length > 0) {
          context.fillStyle = theme.getColor(Theme.Color.Negative);
          Plotter.createRectangles(context, fillNegRects);
          context.fill();
      }
  };
  var OHLCPlotter = create_class(Plotter);
  OHLCPlotter.prototype.__construct = function (name) {
      OHLCPlotter.__super.__construct.call(this, name);
  };
  OHLCPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var ds = mgr.getDataSource(this.getDataSourceName());
      if (!is_instance(ds, MainDataSource) || ds.getDataCount() < 1)
          return;
      var area = mgr.getArea(this.getAreaName());
      var timeline = mgr.getTimeline(this.getDataSourceName());
      var range = mgr.getRange(this.getAreaName());
      if (range.getRange() == 0.0)
          return;
      var theme = mgr.getTheme(this.getFrameName());
      var first = timeline.getFirstIndex();
      var last = timeline.getLastIndex();
      var start;
      if (area.isChanged() || timeline.isUpdated() || range.isUpdated())
          start = first;
      else
          start = Math.max(first, last - 2);
      var cW = timeline.getColumnWidth();
      var iW = timeline.getItemWidth() >> 1;
      var left = timeline.toItemLeft(start);
      var center = timeline.toItemCenter(start);
      var right = left + timeline.getItemWidth();
      var fillPosRects = [];
      var fillUchRects = [];
      var fillNegRects = [];
      for (var i = start; i < last; i++) {
          var data = ds.getDataAt(i);
          var high = range.toY(data.high);
          var low = range.toY(data.low);
          var iH = Math.max(low - high, 1);
          if (data.close > data.open) {
              var top = range.toY(data.close);
              var bottom = range.toY(data.open);
              fillPosRects.push({x: center, y: high, w: 1, h: iH});
              fillPosRects.push({x: left, y: top, w: iW, h: 1});
              fillPosRects.push({x: center, y: bottom, w: iW, h: 1});
          } else if (data.close == data.open) {
              var y = range.toY(data.close);
              fillUchRects.push({x: center, y: high, w: 1, h: iH});
              fillUchRects.push({x: left, y: y, w: iW, h: 1});
              fillUchRects.push({x: center, y: y, w: iW, h: 1});
          } else {
              var top = range.toY(data.open);
              var bottom = range.toY(data.close);
              fillNegRects.push({x: center, y: high, w: 1, h: iH});
              fillNegRects.push({x: left, y: top, w: iW, h: 1});
              fillNegRects.push({x: center, y: bottom, w: iW, h: 1});
          }
          left += cW;
          center += cW;
          right += cW;
      }
      if (fillPosRects.length > 0) {
          context.fillStyle = theme.getColor(Theme.Color.Positive);
          Plotter.createRectangles(context, fillPosRects);
          context.fill();
      }
      if (fillUchRects.length > 0) {
          context.fillStyle = theme.getColor(Theme.Color.Negative);
          Plotter.createRectangles(context, fillUchRects);
          context.fill();
      }
      if (fillNegRects.length > 0) {
          context.fillStyle = theme.getColor(Theme.Color.Negative);
          Plotter.createRectangles(context, fillNegRects);
          context.fill();
      }
  };
  var MainInfoPlotter = create_class(Plotter);
  MainInfoPlotter.prototype.__construct = function (name) {
      MainInfoPlotter.__super.__construct.call(this, name);
  };
  
  function format_time(v) {
      return (v < 10) ? "0" + v.toString() : v.toString();
  }
  
  MainInfoPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getAreaName());
      var timeline = mgr.getTimeline(this.getDataSourceName());
      var ds = mgr.getDataSource(this.getDataSourceName());
      var theme = mgr.getTheme(this.getFrameName());
      context.font = theme.getFont(Theme.Font.Default);
      context.textAlign = "left";
      context.textBaseline = "top";
      context.fillStyle = theme.getColor(Theme.Color.Text4);
      var rect = {
          x: area.getLeft() + 4,
          y: area.getTop() + 2,
          w: area.getWidth() - 8,
          h: 20
      };
      var selIndex = timeline.getSelectedIndex();
      if (selIndex < 0)
          return;
      var data = ds.getDataAt(selIndex);
      var digits = ds.getDecimalDigits();
      var time = new Date(data.date);
      var year = time.getFullYear();
      var month = format_time(time.getMonth() + 1);
      var date = format_time(time.getDate());
      var hour = format_time(time.getHours());
      var minute = format_time(time.getMinutes());
      var lang = mgr.getLanguage();
      if (lang == "zh-cn") {
          if (!Plotter.drawString(context, '时间: ' +
                  year + '-' + month + '-' + date + '  ' + hour + ':' + minute, rect))
              return;
          if (!Plotter.drawString(context, '  开: ' + data.open.toFixed(digits), rect))
              return;
          if (!Plotter.drawString(context, '  高: ' + data.high.toFixed(digits), rect))
              return;
          if (!Plotter.drawString(context, '  低: ' + data.low.toFixed(digits), rect))
              return;
          if (!Plotter.drawString(context, '  收: ' + data.close.toFixed(digits), rect))
              return;
      } else if (lang == "en-us") {
          if (!Plotter.drawString(context, 'DATE: ' +
                  year + '-' + month + '-' + date + '  ' + hour + ':' + minute, rect))
              return;
          if (!Plotter.drawString(context, '  O: ' + data.open.toFixed(digits), rect))
              return;
          if (!Plotter.drawString(context, '  H: ' + data.high.toFixed(digits), rect))
              return;
          if (!Plotter.drawString(context, '  L: ' + data.low.toFixed(digits), rect))
              return;
          if (!Plotter.drawString(context, '  C: ' + data.close.toFixed(digits), rect))
              return;
      } else if (lang == "zh-tw") {
          if (!Plotter.drawString(context, '時間: ' +
                  year + '-' + month + '-' + date + '  ' + hour + ':' + minute, rect))
              return;
          if (!Plotter.drawString(context, '  開: ' + data.open.toFixed(digits), rect))
              return;
          if (!Plotter.drawString(context, '  高: ' + data.high.toFixed(digits), rect))
              return;
          if (!Plotter.drawString(context, '  低: ' + data.low.toFixed(digits), rect))
              return;
          if (!Plotter.drawString(context, '  收: ' + data.close.toFixed(digits), rect))
              return;
      }
      if (selIndex > 0) {
          if (lang == "zh-cn") {
              if (!Plotter.drawString(context, '  涨幅: ', rect))
                  return;
          } else if (lang == "en-us") {
              if (!Plotter.drawString(context, '  CHANGE: ', rect))
                  return;
          } else if (lang == "zh-tw") {
              if (!Plotter.drawString(context, '  漲幅: ', rect))
                  return;
          }
          var prev = ds.getDataAt(selIndex - 1);
          if ((data.close - prev.close) / prev.close * 100.0) {
              var change = (data.close - prev.close) / prev.close * 100.0;
          } else {
              var change = 0.00;
          }
  
  
          if (change >= 0) {
              change = ' ' + change.toFixed(2);
              context.fillStyle = theme.getColor(Theme.Color.TextPositive);
          } else {
              change = change.toFixed(2);
              context.fillStyle = theme.getColor(Theme.Color.TextNegative);
          }
          if (!Plotter.drawString(context, change, rect))
              return;
          context.fillStyle = theme.getColor(Theme.Color.Text4);
          if (!Plotter.drawString(context, ' %', rect))
              return;
      }
      //  debugger;
      if ((data.high - data.low) / data.low * 100.0) {
          var amplitude = (data.high - data.low) / data.low * 100.0;
      } else {
          var amplitude = 0.00;
      }
  
  
      if (lang == "zh-cn") {
          if (!Plotter.drawString(context, '  振幅: ' + amplitude.toFixed(2) + ' %', rect))
              return;
          if (!Plotter.drawString(context, '  量: ' + data.volume.toFixed(2), rect))
              return;
      } else if (lang == "en-us") {
          if (!Plotter.drawString(context, '  AMPLITUDE: ' + amplitude.toFixed(2) + ' %', rect))
              return;
          if (!Plotter.drawString(context, '  V: ' + data.volume.toFixed(2), rect))
              return;
      } else if (lang == "zh-tw") {
          if (!Plotter.drawString(context, '  振幅: ' + amplitude.toFixed(2) + ' %', rect))
              return;
          if (!Plotter.drawString(context, '  量: ' + data.volume.toFixed(2), rect))
              return;
      }
      var dp = mgr.getDataProvider(this.getAreaName() + ".secondary");
      if (dp == undefined)
          return;
      var indic = dp.getIndicator();
      var n, cnt = indic.getOutputCount();
      for (n = 0; n < cnt; n++) {
          var out = indic.getOutputAt(n);
          var v = out.execute(selIndex);
          if (isNaN(v))
              continue;
          var info = "  " + out.getName() + ": " + v.toFixed(digits);
          var color = out.getColor();
          if (color === undefined)
              color = Theme.Color.Indicator0 + n;
          context.fillStyle = theme.getColor(color);
          if (!Plotter.drawString(context, info, rect))
              return;
      }
  };
  var IndicatorPlotter = create_class(NamedObject);
  IndicatorPlotter.prototype.__construct = function (name) {
      IndicatorPlotter.__super.__construct.call(this, name);
  };
  IndicatorPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getAreaName());
      var timeline = mgr.getTimeline(this.getDataSourceName());
      var range = mgr.getRange(this.getAreaName());
      if (range.getRange() == 0.0)
          return;
      var dp = mgr.getDataProvider(this.getName());
      if (!is_instance(dp, IndicatorDataProvider))
          return;
      var theme = mgr.getTheme(this.getFrameName());
      var cW = timeline.getColumnWidth();
      var first = timeline.getFirstIndex();
      var last = timeline.getLastIndex();
      var start;
      if (area.isChanged() || timeline.isUpdated() || range.isUpdated())
          start = first;
      else
          start = Math.max(first, last - 2);
      var indic = dp.getIndicator();
      var out, n, outCount = indic.getOutputCount();
      for (n = 0; n < outCount; n++) {
          out = indic.getOutputAt(n);
          var style = out.getStyle();
          if (style == OutputStyle.VolumeStick) {
              this.drawVolumeStick(context, theme,
                  mgr.getDataSource(this.getDataSourceName()), start, last,
                  timeline.toItemLeft(start), cW, timeline.getItemWidth(), range);
          } else if (style == OutputStyle.MACDStick) {
              this.drawMACDStick(context, theme,
                  out, start, last,
                  timeline.toItemLeft(start), cW, timeline.getItemWidth(), range);
          } else if (style == OutputStyle.SARPoint) {
              this.drawSARPoint(context, theme,
                  out, start, last,
                  timeline.toItemCenter(start), cW, timeline.getItemWidth(), range);
          }
      }
      var left = timeline.toColumnLeft(start);
      var center = timeline.toItemCenter(start);
      context.save();
      context.rect(left, area.getTop(), area.getRight() - left, area.getHeight());
      context.clip();
      context.translate(0.5, 0.5);
      for (n = 0; n < outCount; n++) {
          var x = center;
          out = indic.getOutputAt(n);
          if (out.getStyle() == OutputStyle.Line) {
              var v, points = [];
              if (start > first) {
                  v = out.execute(start - 1);
                  if (isNaN(v) == false)
                      points.push({"x": x - cW, "y": range.toY(v)});
              }
              for (var i = start; i < last; i++, x += cW) {
                  v = out.execute(i);
                  if (isNaN(v) == false)
                      points.push({"x": x, "y": range.toY(v)});
              }
              if (points.length > 0) {
                  var color = out.getColor();
                  if (color == undefined)
                      color = Theme.Color.Indicator0 + n;
                  context.strokeStyle = theme.getColor(color);
                  Plotter.drawLines(context, points);
              }
          }
      }
      context.restore();
  };
  IndicatorPlotter.prototype.drawVolumeStick =
      function (context, theme, ds, first, last, startX, cW, iW, range) {
          var dark = is_instance(theme, DarkTheme);
          var left = startX;
          var bottom = range.toY(0);
          var strokePosRects = [];
          var fillPosRects = [];
          var fillNegRects = [];
          for (var i = first; i < last; i++) {
              var data = ds.getDataAt(i);
              var top = range.toY(data.volume);
              var iH = range.toHeight(data.volume);
              if (data.close > data.open) {
                  if (iH > 1 && iW > 1 && dark) {
                      strokePosRects.push({x: left + 0.5, y: top + 0.5, w: iW - 1, h: iH - 1});
                  } else {
                      fillPosRects.push({x: left, y: top, w: Math.max(iW, 1), h: Math.max(iH, 1)});
                  }
              } else if (data.close == data.open) {
                  if (i > 0 && data.close >= ds.getDataAt(i - 1).close) {
                      if (iH > 1 && iW > 1 && dark) {
                          strokePosRects.push({x: left + 0.5, y: top + 0.5, w: iW - 1, h: iH - 1});
                      } else {
                          fillPosRects.push({x: left, y: top, w: Math.max(iW, 1), h: Math.max(iH, 1)});
                      }
                  } else {
                      fillNegRects.push({x: left, y: top, w: Math.max(iW, 1), h: Math.max(iH, 1)});
                  }
              } else {
                  fillNegRects.push({x: left, y: top, w: Math.max(iW, 1), h: Math.max(iH, 1)});
              }
              left += cW;
          }
          if (strokePosRects.length > 0) {
              context.strokeStyle = theme.getColor(Theme.Color.Positive);
              Plotter.createRectangles(context, strokePosRects);
              context.stroke();
          }
          if (fillPosRects.length > 0) {
              context.fillStyle = theme.getColor(Theme.Color.Positive);
              Plotter.createRectangles(context, fillPosRects);
              context.fill();
          }
          if (fillNegRects.length > 0) {
              context.fillStyle = theme.getColor(Theme.Color.Negative);
              Plotter.createRectangles(context, fillNegRects);
              context.fill();
          }
      };
  IndicatorPlotter.prototype.drawMACDStick =
      function (context, theme, output, first, last, startX, cW, iW, range) {
          var left = startX;
          var middle = range.toY(0);
          var strokePosRects = [];
          var strokeNegRects = [];
          var fillPosRects = [];
          var fillNegRects = [];
          var prevMACD = (first > 0) ? output.execute(first - 1) : NaN;
          for (var i = first; i < last; i++) {
              var MACD = output.execute(i);
              if (MACD >= 0) {
                  var iH = range.toHeight(MACD);
                  if ((i == 0 || MACD >= prevMACD) && iH > 1 && iW > 1)
                      strokePosRects.push({x: left + 0.5, y: middle - iH + 0.5, w: iW - 1, h: iH - 1});
                  else
                      fillPosRects.push({x: left, y: middle - iH, w: Math.max(iW, 1), h: Math.max(iH, 1)});
              } else {
                  var iH = range.toHeight(-MACD);
                  if ((i == 0 || MACD >= prevMACD) && iH > 1 && iW > 1)
                      strokeNegRects.push({x: left + 0.5, y: middle + 0.5, w: iW - 1, h: iH - 1});
                  else
                      fillNegRects.push({x: left, y: middle, w: Math.max(iW, 1), h: Math.max(iH, 1)});
              }
              prevMACD = MACD;
              left += cW;
          }
          if (strokePosRects.length > 0) {
              context.strokeStyle = theme.getColor(Theme.Color.Positive);
              Plotter.createRectangles(context, strokePosRects);
              context.stroke();
          }
          if (strokeNegRects.length > 0) {
              context.strokeStyle = theme.getColor(Theme.Color.Negative);
              Plotter.createRectangles(context, strokeNegRects);
              context.stroke();
          }
          if (fillPosRects.length > 0) {
              context.fillStyle = theme.getColor(Theme.Color.Positive);
              Plotter.createRectangles(context, fillPosRects);
              context.fill();
          }
          if (fillNegRects.length > 0) {
              context.fillStyle = theme.getColor(Theme.Color.Negative);
              Plotter.createRectangles(context, fillNegRects);
              context.fill();
          }
      };
  IndicatorPlotter.prototype.drawSARPoint =
      function (context, theme, output, first, last, startX, cW, iW, range) {
          var r = iW >> 1;
          if (r < 0.5) r = 0.5;
          if (r > 4) r = 4;
          var center = startX;
          var right = center + r;
          var endAngle = 2 * Math.PI;
          context.save();
          context.translate(0.5, 0.5);
          context.strokeStyle = theme.getColor(Theme.Color.Indicator3);
          context.beginPath();
          for (var i = first; i < last; i++) {
              var y = range.toY(output.execute(i));
              context.moveTo(right, y);
              context.arc(center, y, r, 0, endAngle);
              center += cW;
              right += cW;
          }
          context.stroke();
          context.restore();
      };
  var IndicatorInfoPlotter = create_class(Plotter);
  IndicatorInfoPlotter.prototype.__construct = function (name) {
      IndicatorInfoPlotter.__super.__construct.call(this, name);
  };
  IndicatorInfoPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getAreaName());
      var timeline = mgr.getTimeline(this.getDataSourceName());
      var dp = mgr.getDataProvider(this.getAreaName() + ".secondary");
      var theme = mgr.getTheme(this.getFrameName());
      context.font = theme.getFont(Theme.Font.Default);
      context.textAlign = "left";
      context.textBaseline = "top";
      context.fillStyle = theme.getColor(Theme.Color.Text4);
      var rect = {
          x: area.getLeft() + 4,
          y: area.getTop() + 2,
          w: area.getWidth() - 8,
          h: 20
      };
      var indic = dp.getIndicator();
      var title;
      switch (indic.getParameterCount()) {
          case 0:
              title = indic.getName();
              break;
          case 1:
              title = indic.getName() + "(" +
                  indic.getParameterAt(0).getValue() +
                  ")";
              break;
          case 2:
              title = indic.getName() + "(" +
                  indic.getParameterAt(0).getValue() + "," +
                  indic.getParameterAt(1).getValue() +
                  ")";
              break;
          case 3:
              title = indic.getName() + "(" +
                  indic.getParameterAt(0).getValue() + "," +
                  indic.getParameterAt(1).getValue() + "," +
                  indic.getParameterAt(2).getValue() +
                  ")";
              break;
          case 4:
              title = indic.getName() + "(" +
                  indic.getParameterAt(0).getValue() + "," +
                  indic.getParameterAt(1).getValue() + "," +
                  indic.getParameterAt(2).getValue() + "," +
                  indic.getParameterAt(3).getValue() +
                  ")";
              break;
          default:
              return;
      }
      if (!Plotter.drawString(context, title, rect))
          return;
      var selIndex = timeline.getSelectedIndex();
      if (selIndex < 0)
          return;
      var out, v, info, color;
      var n, cnt = indic.getOutputCount();
      for (n = 0; n < cnt; n++) {
          out = indic.getOutputAt(n);
          v = out.execute(selIndex);
          if (isNaN(v))
              continue;
          info = "  " + out.getName() + ": " + v.toFixed(2);
          color = out.getColor();
          if (color === undefined)
              color = Theme.Color.Indicator0 + n;
          context.fillStyle = theme.getColor(color);
          if (!Plotter.drawString(context, info, rect))
              return;
      }
  };
  var MinMaxPlotter = create_class(NamedObject);
  MinMaxPlotter.prototype.__construct = function (name) {
      MinMaxPlotter.__super.__construct.call(this, name);
  };
  MinMaxPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var ds = mgr.getDataSource(this.getDataSourceName());
      if (ds.getDataCount() < 1)
          return;
      var timeline = mgr.getTimeline(this.getDataSourceName());
      if (timeline.getInnerWidth() < timeline.getColumnWidth())
          return;
      var range = mgr.getRange(this.getAreaName());
      if (range.getRange() == 0)
          return;
      var dp = mgr.getDataProvider(this.getAreaName() + ".main");
      var first = timeline.getFirstIndex();
      var center = (first + timeline.getLastIndex()) >> 1;
      var theme = mgr.getTheme(this.getFrameName());
      context.font = theme.getFont(Theme.Font.Default);
      context.textBaseline = "middle";
      context.fillStyle = theme.getColor(Theme.Color.Text4);
      context.strokeStyle = theme.getColor(Theme.Color.Text4);
      var digits = ds.getDecimalDigits();
      this.drawMark(context, dp.getMinValue(), digits, range.toY(dp.getMinValue()),
          first, center, dp.getMinValueIndex(), timeline);
      this.drawMark(context, dp.getMaxValue(), digits, range.toY(dp.getMaxValue()),
          first, center, dp.getMaxValueIndex(), timeline);
  };
  MinMaxPlotter.prototype.drawMark = function (context, v, digits, y, first, center, index, timeline) {
      var arrowStart, arrowStop, _arrowStop;
      var textStart;
      if (index > center) {
          context.textAlign = "right";
          arrowStart = timeline.toItemCenter(index) - 4;
          arrowStop = arrowStart - 7;
          _arrowStop = arrowStart - 3;
          textStart = arrowStop - 4;
      } else {
          context.textAlign = "left";
          arrowStart = timeline.toItemCenter(index) + 4;
          arrowStop = arrowStart + 7;
          _arrowStop = arrowStart + 3;
          textStart = arrowStop + 4;
      }
      Plotter.drawLine(context, arrowStart, y, arrowStop, y);
      Plotter.drawLine(context, arrowStart, y, _arrowStop, y + 2);
      Plotter.drawLine(context, arrowStart, y, _arrowStop, y - 2);
      context.fillText(String.fromFloat(v, digits), textStart, y);
  };
  var TimelinePlotter = create_class(Plotter);
  TimelinePlotter.prototype.__construct = function (name) {
      TimelinePlotter.__super.__construct.call(this, name);
  };
  TimelinePlotter.TP_MINUTE = 60 * 1000;
  TimelinePlotter.TP_HOUR = 60 * TimelinePlotter.TP_MINUTE;
  TimelinePlotter.TP_DAY = 24 * TimelinePlotter.TP_HOUR;
  TimelinePlotter.TIME_INTERVAL = [
      5 * TimelinePlotter.TP_MINUTE,
      10 * TimelinePlotter.TP_MINUTE,
      15 * TimelinePlotter.TP_MINUTE,
      30 * TimelinePlotter.TP_MINUTE,
      TimelinePlotter.TP_HOUR,
      2 * TimelinePlotter.TP_HOUR,
      3 * TimelinePlotter.TP_HOUR,
      6 * TimelinePlotter.TP_HOUR,
      12 * TimelinePlotter.TP_HOUR,
      TimelinePlotter.TP_DAY,
      2 * TimelinePlotter.TP_DAY
  ];
  TimelinePlotter.MonthConvert = {
      1: "Jan.",
      2: "Feb.",
      3: "Mar.",
      4: "Apr.",
      5: "May.",
      6: "Jun.",
      7: "Jul.",
      8: "Aug.",
      9: "Sep.",
      10: "Oct.",
      11: "Nov.",
      12: "Dec."
  };
  TimelinePlotter.prototype.Draw = function (context) {
  
  
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getAreaName());
      var timeline = mgr.getTimeline(this.getDataSourceName());
      if (!area.isChanged() && !timeline.isUpdated())
          return;
      var ds = mgr.getDataSource(this.getDataSourceName());
      if (ds.getDataCount() < 2)
          return;
      var timeInterval = ds.getDataAt(1).date - ds.getDataAt(0).date;
      var n, cnt = TimelinePlotter.TIME_INTERVAL.length;
      for (n = 0; n < cnt; n++) {
          if (timeInterval < TimelinePlotter.TIME_INTERVAL[n])
              break;
      }
      for (; n < cnt; n++) {
          if (TimelinePlotter.TIME_INTERVAL[n] % timeInterval == 0)
              if ((TimelinePlotter.TIME_INTERVAL[n] / timeInterval) * timeline.getColumnWidth() > 60)
                  break;
      }
      var first = timeline.getFirstIndex();
      var last = timeline.getLastIndex();
      var d = new Date();
      var local_utc_diff = d.getTimezoneOffset() * 60 * 1000;
      var theme = mgr.getTheme(this.getFrameName());
      context.font = theme.getFont(Theme.Font.Default);
      context.textAlign = "center";
      context.textBaseline = "middle";
      var lang = mgr.getLanguage();
      var gridRects = [];
      var top = area.getTop();
      var middle = area.getMiddle();
      for (var i = first; i < last; i++) {
          var utcDate = ds.getDataAt(i).date;
          var localDate = utcDate - local_utc_diff;
          var time = new Date(utcDate);
          var year = time.getFullYear();
          var month = time.getMonth() + 1;
          var date = time.getDate();
          var hour = time.getHours();
          var minute = time.getMinutes();
          var text = "";
          if (n < cnt) {
              var m = Math.max(
                  TimelinePlotter.TP_DAY,
                  TimelinePlotter.TIME_INTERVAL[n]);
              if (localDate % m == 0) {
                  if (lang == "zh-cn")
                      text = month.toString() + "月" + date.toString() + "日";
                  else if (lang == "zh-tw")
                      text = month.toString() + "月" + date.toString() + "日";
                  else if (lang == "en-us")
                      text = TimelinePlotter.MonthConvert[month] + " " + date.toString();
                  context.fillStyle = theme.getColor(Theme.Color.Text4);
              } else if (localDate % TimelinePlotter.TIME_INTERVAL[n] == 0) {
                  var strMinute = minute.toString();
                  if (minute < 10)
                      strMinute = "0" + strMinute;
                  text = hour.toString() + ":" + strMinute;
                  context.fillStyle = theme.getColor(Theme.Color.Text2);
              }
          } else if (date == 1 && (hour < (timeInterval / TimelinePlotter.TP_HOUR))) {
              if (month == 1) {
                  text = year.toString();
                  if (lang == "zh-cn")
                      text += "年";
                  else if (lang == "zh-tw")
                      text += "年";
              } else {
                  if (lang == "zh-cn")
                      text = month.toString() + "月";
                  else if (lang == "zh-tw")
                      text = month.toString() + "月";
                  else if (lang == "en-us")
                      text = TimelinePlotter.MonthConvert[month];
              }
              context.fillStyle = theme.getColor(Theme.Color.Text4);
          }
          if (text.length > 0) {
              var x = timeline.toItemCenter(i);
              gridRects.push({x: x, y: top, w: 1, h: 4});
              context.fillText(text, x, middle);
          }
      }
      if (gridRects.length > 0) {
          context.fillStyle = theme.getColor(Theme.Color.Grid1);
          Plotter.createRectangles(context, gridRects);
          context.fill();
      }
  };
  var RangePlotter = create_class(NamedObject);
  RangePlotter.prototype.__construct = function (name) {
      RangePlotter.__super.__construct.call(this, name);
  };
  RangePlotter.prototype.getRequiredWidth = function (context, v) {
      var mgr = ChartManager.getInstance();
      var theme = mgr.getTheme(this.getFrameName());
      context.font = theme.getFont(Theme.Font.Default);
      return context.measureText((Math.floor(v) + 0.88).toString()).width + 16;
  };
  RangePlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var areaName = this.getAreaName();
      var area = mgr.getArea(areaName);
      var rangeName = areaName.substring(0, areaName.lastIndexOf("Range"));
      var range = mgr.getRange(rangeName);
      if (range.getRange() == 0.0)
          return;
      var isMainRange = range.getNameObject().getCompAt(2) == "main";
      if (isMainRange) {
      } else {
          if (!area.isChanged() && !range.isUpdated())
              return;
      }
      var gradations = range.getGradations();
      if (gradations.length == 0)
          return;
      var left = area.getLeft();
      var right = area.getRight();
      var center = area.getCenter();
      var theme = mgr.getTheme(this.getFrameName());
      context.font = theme.getFont(Theme.Font.Default);
      context.textAlign = "center";
      context.textBaseline = "middle";
      context.fillStyle = theme.getColor(Theme.Color.Text2);
      var gridRects = [];
      for (var n in gradations) {
          var y = range.toY(gradations[n]);
          gridRects.push({x: left, y: y, w: 6, h: 1});
          gridRects.push({x: right - 6, y: y, w: 6, h: 1});
          context.fillText(String.fromFloat(gradations[n], 2), center, y);
      }
      if (gridRects.length > 0) {
          context.fillStyle = theme.getColor(Theme.Color.Grid1);
          Plotter.createRectangles(context, gridRects);
          context.fill();
      }
  };
  
  var COrderGraphPlotter = create_class(NamedObject);
  COrderGraphPlotter.prototype.__construct = function (name) {
      COrderGraphPlotter.__super.__construct.call(this, name);
  };
  COrderGraphPlotter.prototype.Draw = function (context) {
      return this._Draw_(context);
  };
  COrderGraphPlotter.prototype._Draw_ = function (context) {
      if (this.Update() == false) return;
      if (this.updateData() == false) return;
      this.m_top = this.m_pArea.getTop();
      this.m_bottom = this.m_pArea.getBottom();
      this.m_left = this.m_pArea.getLeft();
      this.m_right = this.m_pArea.getRight();
      context.save();
      context.rect(this.m_left, this.m_top, this.m_right - this.m_left, this.m_bottom - this.m_top);
      context.clip();
      var all = ChartManager.getInstance().getChart()._depthData;
      this.x_offset = 0;
      this.y_offset = 0;
      var ask_tmp = {};
      var bid_tmp = {};
      ask_tmp.x = this.m_left + all.array[this.m_ask_si].amounts * this.m_Step;
      ask_tmp.y = this.m_pRange.toY(all.array[this.m_ask_si].rate);
      bid_tmp.x = this.m_left + all.array[this.m_bid_si].amounts * this.m_Step;
      bid_tmp.y = this.m_pRange.toY(all.array[this.m_bid_si].rate);
      if (Math.abs(ask_tmp.y - bid_tmp.y) < 1) {
          this.y_offset = 1;
      }
      this.x_offset = 1;
      this.DrawBackground(context);
      this.UpdatePoints();
      this.FillBlack(context);
      this.DrawGradations(context);
      this.DrawLine(context);
      context.restore();
  };
  COrderGraphPlotter.prototype.DrawBackground = function (context) {
      context.fillStyle = this.m_pTheme.getColor(Theme.Color.Background);
      context.fillRect(this.m_left, this.m_top, this.m_right - this.m_left, this.m_bottom - this.m_top);
      var all = ChartManager.getInstance().getChart()._depthData;
      if (this.m_mode == 0) {
          var ask_bottom = this.m_pRange.toY(all.array[this.m_ask_si].rate) - this.y_offset;
          var bid_top = this.m_pRange.toY(all.array[this.m_bid_si].rate) + this.y_offset;
          var ask_gradient = context.createLinearGradient(this.m_left, 0, this.m_right, 0);
          ask_gradient.addColorStop(0, this.m_pTheme.getColor(Theme.Color.Background));
          ask_gradient.addColorStop(1, this.m_pTheme.getColor(Theme.Color.PositiveDark));
          context.fillStyle = ask_gradient;
          context.fillRect(this.m_left, this.m_top, this.m_right - this.m_left, ask_bottom - this.m_top);
          var bid_gradient = context.createLinearGradient(this.m_left, 0, this.m_right, 0);
          bid_gradient.addColorStop(0, this.m_pTheme.getColor(Theme.Color.Background));
          bid_gradient.addColorStop(1, this.m_pTheme.getColor(Theme.Color.NegativeDark));
          context.fillStyle = bid_gradient;
          context.fillRect(this.m_left, bid_top, this.m_right - this.m_left, this.m_bottom - bid_top);
      } else if (this.m_mode == 1) {
          var ask_gradient = context.createLinearGradient(this.m_left, 0, this.m_right, 0);
          ask_gradient.addColorStop(0, this.m_pTheme.getColor(Theme.Color.Background));
          ask_gradient.addColorStop(1, this.m_pTheme.getColor(Theme.Color.PositiveDark));
          context.fillStyle = ask_gradient;
          context.fillRect(this.m_left, this.m_top, this.m_right - this.m_left, this.m_bottom - this.m_top);
      } else if (this.m_mode == 2) {
          var bid_gradient = context.createLinearGradient(this.m_left, 0, this.m_right, 0);
          bid_gradient.addColorStop(0, this.m_pTheme.getColor(Theme.Color.Background));
          bid_gradient.addColorStop(1, this.m_pTheme.getColor(Theme.Color.NegativeDark));
          context.fillStyle = bid_gradient;
          context.fillRect(this.m_left, this.m_top, this.m_right - this.m_left, this.m_bottom - this.m_top);
      }
  };
  COrderGraphPlotter.prototype.DrawLine = function (context) {
      if (this.m_mode == 0 || this.m_mode == 1) {
          context.strokeStyle = this.m_pTheme.getColor(Theme.Color.Positive);
          context.beginPath();
          context.moveTo(Math.floor(this.m_ask_points[0].x) + 0.5, Math.floor(this.m_ask_points[0].y) + 0.5);
          for (var i = 1; i < this.m_ask_points.length; i++) {
              context.lineTo(Math.floor(this.m_ask_points[i].x) + 0.5, Math.floor(this.m_ask_points[i].y) + 0.5);
          }
          context.stroke();
      }
      if (this.m_mode == 0 || this.m_mode == 2) {
          context.strokeStyle = this.m_pTheme.getColor(Theme.Color.Negative);
          context.beginPath();
          context.moveTo(this.m_bid_points[0].x + 0.5, this.m_bid_points[0].y + 0.5);
          for (var i = 1; i < this.m_bid_points.length; i++) {
              context.lineTo(this.m_bid_points[i].x + 0.5, this.m_bid_points[i].y + 0.5);
          }
          context.stroke();
      }
  };
  COrderGraphPlotter.prototype.UpdatePoints = function () {
      var all = ChartManager.getInstance().getChart()._depthData;
      this.m_ask_points = [];
      var index_ask = {};
      index_ask.x = Math.floor(this.m_left);
      index_ask.y = Math.floor(this.m_pRange.toY(all.array[this.m_ask_si].rate) - this.y_offset);
      this.m_ask_points.push(index_ask);
      var ask_p_i = 0;
      for (var i = this.m_ask_si; i >= this.m_ask_ei; i--) {
          var index0 = {};
          var index1 = {};
          if (i == this.m_ask_si) {
              index0.x = Math.floor(this.m_left + all.array[i].amounts * this.m_Step + this.x_offset);
              index0.y = Math.floor(this.m_pRange.toY(all.array[i].rate) - this.y_offset);
              this.m_ask_points.push(index0);
              ask_p_i = 1;
          } else {
              index0.x = Math.floor(this.m_left + all.array[i].amounts * this.m_Step + this.x_offset);
              index0.y = Math.floor(this.m_ask_points[ask_p_i].y);
              index1.x = Math.floor(index0.x);
              index1.y = Math.floor(this.m_pRange.toY(all.array[i].rate) - this.y_offset);
              this.m_ask_points.push(index0);
              ask_p_i++;
              this.m_ask_points.push(index1);
              ask_p_i++;
          }
      }
      this.m_bid_points = [];
      var index_bid = {};
      index_bid.x = Math.floor(this.m_left);
      index_bid.y = Math.ceil(this.m_pRange.toY(all.array[this.m_bid_si].rate) + this.y_offset);
      this.m_bid_points.push(index_bid);
      var bid_p_i = 0;
      for (var i = this.m_bid_si; i <= this.m_bid_ei; i++) {
          var index0 = {};
          var index1 = {};
          if (i == this.m_bid_si) {
              index0.x = Math.floor(this.m_left + all.array[i].amounts * this.m_Step + this.x_offset);
              index0.y = Math.ceil(this.m_pRange.toY(all.array[i].rate) + this.y_offset);
              this.m_bid_points.push(index0);
              bid_p_i = 1;
          } else {
              index0.x = Math.floor(this.m_left + all.array[i].amounts * this.m_Step + this.x_offset);
              index0.y = Math.ceil(this.m_bid_points[bid_p_i].y);
              index1.x = Math.floor(index0.x);
              index1.y = Math.ceil(this.m_pRange.toY(all.array[i].rate) + this.x_offset);
              this.m_bid_points.push(index0);
              bid_p_i++;
              this.m_bid_points.push(index1);
              bid_p_i++;
          }
      }
  };
  COrderGraphPlotter.prototype.updateData = function () {
      var all = ChartManager.getInstance().getChart()._depthData;
      if (all.array == null) return false;
      if (all.array.length <= 50) return false;
      var minRange = this.m_pRange.getOuterMinValue();
      var maxRange = this.m_pRange.getOuterMaxValue();
      this.m_ask_si = all.asks_si;
      this.m_ask_ei = all.asks_si;
      for (var i = all.asks_si; i >= all.asks_ei; i--) {
          if (all.array[i].rate < maxRange)
              this.m_ask_ei = i;
          else
              break;
      }
      this.m_bid_si = all.bids_si;
      this.m_bid_ei = all.bids_si;
      for (var i = all.bids_si; i <= all.bids_ei; i++) {
          if (all.array[i].rate > minRange)
              this.m_bid_ei = i;
          else
              break;
      }
      if (this.m_ask_ei == this.m_ask_si)
          this.m_mode = 2;
      else if (this.m_bid_ei == this.m_bid_si)
          this.m_mode = 1;
      else
          this.m_mode = 0;
      this.m_Step = this.m_pArea.getWidth();
      if (this.m_mode == 0) {
          /*
           * View: B     --------    T
           * Data: Lo      -----     Hi
           */
          if (this.m_ask_ei == all.asks_ei && this.m_bid_ei == all.bids_ei) {
              this.m_Step /= Math.min(all.array[this.m_ask_ei].amounts,
                  all.array[this.m_bid_ei].amounts);
          }
          /*
           * View: B     --------     T
           * Data: Lo         -----   Hi
           */
          else if (this.m_ask_ei != all.asks_ei && this.m_bid_ei == all.bids_ei) {
              this.m_Step /= all.array[this.m_bid_ei].amounts;
          }
          /*
           * View: B     --------    T
           * Data: Lo  -----         Hi
           */
          else if (this.m_ask_ei == all.asks_ei && this.m_bid_ei != all.bids_ei) {
              this.m_Step /= all.array[this.m_ask_ei].amounts;
          }
          /*
           * View: B     --------    T
           * Data: Lo  ------------  Hi
           */
          else if (this.m_ask_ei != all.asks_ei && this.m_bid_ei != all.bids_ei) {
              this.m_Step /= Math.max(all.array[this.m_ask_ei].amounts,
                  all.array[this.m_bid_ei].amounts);
          }
      } else if (this.m_mode == 1) {
          this.m_Step /= all.array[this.m_ask_ei].amounts;
      } else if (this.m_mode == 2) {
          this.m_Step /= all.array[this.m_bid_ei].amounts;
      }
      return true;
  };
  COrderGraphPlotter.prototype.Update = function () {
      this.m_pMgr = ChartManager.getInstance();
      var areaName = this.getAreaName();
      this.m_pArea = this.m_pMgr.getArea(areaName);
      if (this.m_pArea == null)
          return false;
      var rangeName = areaName.substring(0, areaName.lastIndexOf("Range"));
      this.m_pRange = this.m_pMgr.getRange(rangeName);
      if (this.m_pRange == null || this.m_pRange.getRange() == 0.0)
          return false;
      this.m_pTheme = this.m_pMgr.getTheme(this.getFrameName());
      if (this.m_pTheme == null)
          return false;
      return true;
  };
  COrderGraphPlotter.prototype.DrawGradations = function (context) {
      var mgr = ChartManager.getInstance();
      var areaName = this.getAreaName();
      var area = mgr.getArea(areaName);
      var rangeName = areaName.substring(0, areaName.lastIndexOf("Range"));
      var range = mgr.getRange(rangeName);
      if (range.getRange() == 0.0)
          return;
      var gradations = range.getGradations();
      if (gradations.length == 0)
          return;
      var left = area.getLeft();
      var right = area.getRight();
      var gridRects = [];
      for (var n in gradations) {
          var y = range.toY(gradations[n]);
          gridRects.push({x: left, y: y, w: 6, h: 1});
          gridRects.push({x: right - 6, y: y, w: 6, h: 1});
      }
      if (gridRects.length > 0) {
          var theme = mgr.getTheme(this.getFrameName());
          context.fillStyle = theme.getColor(Theme.Color.Grid1);
          Plotter.createRectangles(context, gridRects);
          context.fill();
      }
  };
  COrderGraphPlotter.prototype.FillBlack = function (context) {
      var ask_point = this.m_ask_points;
      var bid_point = this.m_bid_points;
      var ask_first_add = {};
      var ask_last_add = {};
      ask_first_add.x = this.m_right;
      ask_first_add.y = ask_point[0].y;
      ask_last_add.x = this.m_right;
      ask_last_add.y = ask_point[ask_point.length - 1].y;
      var bid_first_add = {};
      var bid_last_add = {};
      bid_first_add.x = this.m_right;
      bid_first_add.y = bid_point[0].y - 1;
      bid_last_add.x = this.m_right;
      bid_last_add.y = bid_point[bid_point.length - 1].y;
      ask_point.unshift(ask_first_add);
      ask_point.push(ask_last_add);
      bid_point.unshift(bid_first_add);
      bid_point.push(bid_last_add);
      context.fillStyle = this.m_pTheme.getColor(Theme.Color.Background);
      context.beginPath();
      context.moveTo(Math.floor(ask_point[0].x) + 0.5, Math.floor(ask_point[0].y) + 0.5);
      for (var i = 1; i < ask_point.length; i++) {
          context.lineTo(Math.floor(ask_point[i].x) + 0.5, Math.floor(ask_point[i].y) + 0.5);
      }
      context.fill();
      context.beginPath();
      context.moveTo(Math.floor(bid_point[0].x) + 0.5, Math.floor(bid_point[0].y) + 0.5);
      for (var i = 1; i < bid_point.length; i++) {
          context.lineTo(Math.floor(bid_point[i].x) + 0.5, Math.floor(bid_point[i].y) + 0.5);
      }
      context.fill();
      ask_point.shift();
      ask_point.pop();
      bid_point.shift();
      bid_point.pop();
  };
  COrderGraphPlotter.prototype.DrawTickerGraph = function (context) {
      return;
      var mgr = ChartManager.getInstance();
      var ds = mgr.getDataSource(this.getDataSourceName());
      var ticker = ds._dataItems[ds._dataItems.length - 1].close;
      var p1x = this.m_left + 1;
      var p1y = this.m_pRange.toY(ticker);
      var p2x = p1x + 5;
      var p2y = p1y + 2.5;
      var p3x = p1x + 5;
      var p3y = p1y - 2.5;
      context.fillStyle = this.m_pTheme.getColor(Theme.Color.Mark);
      context.strokeStyle = this.m_pTheme.getColor(Theme.Color.Mark);
  };
  var LastVolumePlotter = create_class(Plotter);
  LastVolumePlotter.prototype.__construct = function (name) {
      LastVolumePlotter.__super.__construct.call(this, name);
  };
  LastVolumePlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var timeline = mgr.getTimeline(this.getDataSourceName());
      var areaName = this.getAreaName();
      var area = mgr.getArea(areaName);
      var rangeName = areaName.substring(0, areaName.lastIndexOf("Range"));
      var range = mgr.getRange(rangeName);
      if (range.getRange() == 0.0)
          return;
      var ds = mgr.getDataSource(this.getDataSourceName());
      if (ds.getDataCount() < 1)
          return;
      var theme = mgr.getTheme(this.getFrameName());
      context.font = theme.getFont(Theme.Font.Default);
      context.textAlign = "left";
      context.textBaseline = "middle";
      context.fillStyle = theme.getColor(Theme.Color.RangeMark);
      context.strokeStyle = theme.getColor(Theme.Color.RangeMark);
      var v = ds.getDataAt(ds.getDataCount() - 1).volume;
      var y = range.toY(v);
      var left = area.getLeft() + 1;
      Plotter.drawLine(context, left, y, left + 7, y);
      Plotter.drawLine(context, left, y, left + 3, y + 2);
      Plotter.drawLine(context, left, y, left + 3, y - 2);
      context.fillText(String.fromFloat(v, 2), left + 10, y);
  };
  /**
   * Created by Administrator on 2014/11/28.
   */
  var LastClosePlotter = create_class(Plotter);
  LastClosePlotter.prototype.__construct = function (name) {
      LastClosePlotter.__super.__construct.call(this, name);
  };
  LastClosePlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var timeline = mgr.getTimeline(this.getDataSourceName());
      var areaName = this.getAreaName();
      var area = mgr.getArea(areaName);
      var rangeName = areaName.substring(0, areaName.lastIndexOf("Range"));
      var range = mgr.getRange(rangeName);
      if (range.getRange() == 0.0)
          return;
      var ds = mgr.getDataSource(this.getDataSourceName());
      if (ds.getDataCount() < 1)
          return;
      var v = ds._dataItems[ds._dataItems.length - 1].close;
      if (v <= range.getMinValue() || v >= range.getMaxValue())
          return;
      var theme = mgr.getTheme(this.getFrameName());
      context.font = theme.getFont(Theme.Font.Default);
      context.textAlign = "left";
      context.textBaseline = "middle";
      context.fillStyle = theme.getColor(Theme.Color.RangeMark);
      context.strokeStyle = theme.getColor(Theme.Color.RangeMark);
      var y = range.toY(v);
      var left = area.getLeft() + 1;
      Plotter.drawLine(context, left, y, left + 7, y);
      Plotter.drawLine(context, left, y, left + 3, y + 2);
      Plotter.drawLine(context, left, y, left + 3, y - 2);
      context.fillText(String.fromFloat(v, ds.getDecimalDigits()), left + 10, y);
  };
  var SelectionPlotter = create_class(Plotter);
  SelectionPlotter.prototype.__construct = function (name) {
      SelectionPlotter.__super.__construct.call(this, name);
  };
  SelectionPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      if (mgr._drawingTool != ChartManager.DrawingTool.CrossCursor)
          return;
      var area = mgr.getArea(this.getAreaName());
      var timeline = mgr.getTimeline(this.getDataSourceName());
      if (timeline.getSelectedIndex() < 0) {
          return;
      }
      var range = mgr.getRange(this.getAreaName());
      var theme = mgr.getTheme(this.getFrameName());
      context.strokeStyle = theme.getColor(Theme.Color.Cursor);
      var x = timeline.toItemCenter(timeline.getSelectedIndex());
      Plotter.drawLine(context, x, area.getTop() - 1, x, area.getBottom());
      var pos = range.getSelectedPosition();
      if (pos >= 0)
          Plotter.drawLine(context, area.getLeft(), pos, area.getRight(), pos);
  };
  var TimelineSelectionPlotter = create_class(NamedObject);
  TimelineSelectionPlotter.MonthConvert = {
      1: "Jan.",
      2: "Feb.",
      3: "Mar.",
      4: "Apr.",
      5: "May.",
      6: "Jun.",
      7: "Jul.",
      8: "Aug.",
      9: "Sep.",
      10: "Oct.",
      11: "Nov.",
      12: "Dec."
  };
  TimelineSelectionPlotter.prototype.__construct = function (name) {
      TimelineSelectionPlotter.__super.__construct.call(this, name);
  };
  TimelineSelectionPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var area = mgr.getArea(this.getAreaName());
      var timeline = mgr.getTimeline(this.getDataSourceName());
      if (timeline.getSelectedIndex() < 0)
          return;
      var ds = mgr.getDataSource(this.getDataSourceName());
      if (!is_instance(ds, MainDataSource))
          return;
      var theme = mgr.getTheme(this.getFrameName());
      var lang = mgr.getLanguage();
      var x = timeline.toItemCenter(timeline.getSelectedIndex());
      context.fillStyle = theme.getColor(Theme.Color.Background);
      context.fillRect(x - 52.5, area.getTop() + 2.5, 106, 18);
      context.strokeStyle = theme.getColor(Theme.Color.Grid3);
      context.strokeRect(x - 52.5, area.getTop() + 2.5, 106, 18);
      context.font = theme.getFont(Theme.Font.Default);
      context.textAlign = "center";
      context.textBaseline = "middle";
      context.fillStyle = theme.getColor(Theme.Color.Text4);
      var time = new Date(ds.getDataAt(timeline.getSelectedIndex()).date);
      var month = time.getMonth() + 1;
      var date = time.getDate();
      var hour = time.getHours();
      var minute = time.getMinutes();
      var second = time.getSeconds();
      var strMonth = month.toString();
      var strDate = date.toString();
      var strHour = hour.toString();
      var strMinute = minute.toString();
      var strSecond = second.toString();
      if (minute < 10)
          strMinute = "0" + strMinute;
      if (second < 10)
          strSecond = "0" + strSecond;
      var text = "";
      if (lang == "zh-cn") {
          text = strMonth + "月" + strDate + "日  " +
              strHour + ":" + strMinute;
      } else if (lang == "zh-tw") {
          text = strMonth + "月" + strDate + "日  " +
              strHour + ":" + strMinute;
      } else if (lang == "en-us") {
          text = TimelineSelectionPlotter.MonthConvert[month] + " " + strDate + "  " +
              strHour + ":" + strMinute;
      }
      if (KlineIns.range < 60000) {
          text += ":" + strSecond;
      }
      context.fillText(text, x, area.getMiddle());
  };
  var RangeSelectionPlotter = create_class(NamedObject);
  RangeSelectionPlotter.prototype.__construct = function (name) {
      RangeSelectionPlotter.__super.__construct.call(this, name);
  };
  RangeSelectionPlotter.prototype.Draw = function (context) {
      var mgr = ChartManager.getInstance();
      var areaName = this.getAreaName();
      var area = mgr.getArea(areaName);
      var timeline = mgr.getTimeline(this.getDataSourceName());
      if (timeline.getSelectedIndex() < 0)
          return;
      var rangeName = areaName.substring(0, areaName.lastIndexOf("Range"));
      var range = mgr.getRange(rangeName);
      if (range.getRange() == 0.0 || range.getSelectedPosition() < 0)
          return;
      var v = range.getSelectedValue();
      if (v == -Number.MAX_VALUE)
          return;
      var y = range.getSelectedPosition();
      Plotter.createPolygon(context, [
          {"x": area.getLeft(), "y": y},
          {"x": area.getLeft() + 5, "y": y + 10},
          {"x": area.getRight() - 3, "y": y + 10},
          {"x": area.getRight() - 3, "y": y - 10},
          {"x": area.getLeft() + 5, "y": y - 10}
      ]);
      var theme = mgr.getTheme(this.getFrameName());
      context.fillStyle = theme.getColor(Theme.Color.Background);
      context.fill();
      context.strokeStyle = theme.getColor(Theme.Color.Grid4);
      context.stroke();
      context.font = theme.getFont(Theme.Font.Default);
      context.textAlign = "center";
      context.textBaseline = "middle";
      context.fillStyle = theme.getColor(Theme.Color.Text3);
      var digits = 2;
      if (range.getNameObject().getCompAt(2) == "main")
          digits = mgr.getDataSource(this.getDataSourceName()).getDecimalDigits();
      context.fillText(String.fromFloat(v, digits), area.getCenter(), y);
  };
  var ChartSettings = {};
  ChartSettings.checkVersion = function () {
      if (ChartSettings._data.ver < 2) {
          ChartSettings._data.ver = 2;
          var charts = ChartSettings._data.charts;
          charts.period_weight = {};
          charts.period_weight['line'] = 8;
          charts.period_weight['1min'] = 7;
          charts.period_weight['5min'] = 6;
          charts.period_weight['15min'] = 5;
          charts.period_weight['30min'] = 4;
          charts.period_weight['1hour'] = 3;
          charts.period_weight['1day'] = 2;
          charts.period_weight['1week'] = 1;
          charts.period_weight['3min'] = 0;
          charts.period_weight['2hour'] = 0;
          charts.period_weight['4hour'] = 0;
          charts.period_weight['6hour'] = 0;
          charts.period_weight['12hour'] = 0;
          charts.period_weight['3day'] = 0;
      }
      if (ChartSettings._data.ver < 3) {
          ChartSettings._data.ver = 3;
          var charts = ChartSettings._data.charts;
          charts.areaHeight = [];
      }
  };
  ChartSettings.get = function () {
      if (ChartSettings._data == undefined) {
          ChartSettings.init();
          ChartSettings.load();
          ChartSettings.checkVersion();
      }
      return ChartSettings._data;
  };
  ChartSettings.init = function () {
      var _indic_param = {};
      var _name = new Array('MA', 'EMA', 'VOLUME', 'MACD', 'KDJ', 'StochRSI', 'RSI', 'DMI', 'OBV', 'BOLL', 'DMA', 'TRIX', 'BRAR', 'VR', 'EMV', 'WR', 'ROC', 'MTM', 'PSY');
      for (var i = 0; i < _name.length; i++) {
          var _value = ChartManager.getInstance().createIndicatorAndRange('', _name[i], true);
          if (_value == null) continue;
          _indic_param[_name[i]] = [];
          var param = _value.indic.getParameters();
          for (var j = 0; j < param.length; j++) {
              _indic_param[_name[i]].push(param[j]);
          }
      }
      var _chart_style = 'CandleStick';
      var _m_indic = 'MA';
      var _indic = new Array('VOLUME', 'MACD');
      var _range = '15m';
      var _frame = {};
      _frame.chartStyle = _chart_style;
      _frame.mIndic = _m_indic;
      _frame.indics = _indic;
      _frame.indicsStatus = 'open';
      _frame.period = _range;
      ChartSettings._data = {
          ver: 1,
          charts: _frame,
          indics: _indic_param,
          theme: "Dark"
      };
      ChartSettings.checkVersion();
  };
  ChartSettings.load = function () {
      if (document.cookie.length <= 0)
          return;
      var start = document.cookie.indexOf("chartSettings=");
      if (start < 0)
          return;
      start += "chartSettings=".length;
      var end = document.cookie.indexOf(";", start);
      if (end < 0)
          end = document.cookie.length;
      var json = unescape(document.cookie.substring(start, end));
      ChartSettings._data = JSON.parse(json);
  };
  ChartSettings.save = function () {
      var exdate = new Date();
      exdate.setDate(exdate.getDate() + 2);
      document.cookie = "chartSettings=" + escape(JSON.stringify(ChartSettings._data)) +
          ";expires=" + exdate.toGMTString();
  };
  var CPoint = create_class(NamedObject);
  CPoint.state = {
      Hide: 0,
      Show: 1,
      Highlight: 2
  };
  CPoint.prototype.__construct = function (name) {
      CPoint.__super.__construct.call(this, name);
      this.pos = {index: -1, value: -1};
      this.state = CPoint.state.Hide;
  };
  CPoint.prototype.getChartObjects = function () {
      var ppMgr = ChartManager.getInstance();
      var ppCDS = ppMgr.getDataSource("frame0.k0");
      if (ppCDS == null || !is_instance(ppCDS, MainDataSource))
          return null;
      var ppTimeline = ppMgr.getTimeline("frame0.k0");
      if (ppTimeline == null)
          return null;
      var ppRange = ppMgr.getRange("frame0.k0.main");
      if (ppRange == null)
          return null;
      return {pMgr: ppMgr, pCDS: ppCDS, pTimeline: ppTimeline, pRange: ppRange};
  };
  CPoint.prototype.setPosXY = function (x, y) {
      var pObj = this.getChartObjects();
      var i = pObj.pTimeline.toIndex(x);
      var v = pObj.pRange.toValue(y);
      var result = this.snapValue(i, v);
      if (result != null)
          v = result;
      this.setPosIV(i, v);
  };
  CPoint.prototype.setPosXYNoSnap = function (x, y) {
      var pObj = this.getChartObjects();
      var i = pObj.pTimeline.toIndex(x);
      var v = pObj.pRange.toValue(y);
      this.setPosIV(i, v);
  };
  CPoint.prototype.setPosIV = function (i, v) {
      this.pos = {index: i, value: v};
  };
  CPoint.prototype.getPosXY = function () {
      var pObj = this.getChartObjects();
      var _x = pObj.pTimeline.toItemCenter(this.pos.index);
      var _y = pObj.pRange.toY(this.pos.value);
      return {x: _x, y: _y};
  };
  CPoint.prototype.getPosIV = function () {
      return {i: this.pos.index, v: this.pos.value};
  };
  CPoint.prototype.setState = function (s) {
      this.state = s;
  };
  CPoint.prototype.getState = function () {
      return this.state;
  };
  CPoint.prototype.isSelected = function (x, y) {
      var xy = this.getPosXY();
      if (x < xy.x - 4 || x > xy.x + 4 || y < xy.y - 4 || y > xy.y + 4)
          return false;
      this.setState(CPoint.state.Highlight);
      return true;
  };
  CPoint.prototype.snapValue = function (i, v) {
      var pObj = this.getChartObjects();
      var result = null;
      var first = Math.floor(pObj.pTimeline.getFirstIndex());
      var last = Math.floor(pObj.pTimeline.getLastIndex());
      if (i < first || i > last)
          return result;
      var y = pObj.pRange.toY(v);
      var pData = pObj.pCDS.getDataAt(i);
      if (pData == null || pData == undefined)
          return result;
      var pDataPre = null;
      if (i > 0)
          pDataPre = pObj.pCDS.getDataAt(i - 1);
      else
          pDataPre = pObj.pCDS.getDataAt(i);
      var candleStickStyle = pObj.pMgr.getChartStyle(pObj.pCDS.getFrameName());
      var open = pObj.pRange.toY(pData.open);
      var high = pObj.pRange.toY(pData.high);
      var low = pObj.pRange.toY(pData.low);
      var close = pObj.pRange.toY(pData.close);
      if (candleStickStyle === "CandleStickHLC") {
          open = pObj.pRange.toY(pDataPre.close);
      }
      var dif_open = Math.abs(open - y);
      var dif_high = Math.abs(high - y);
      var dif_low = Math.abs(low - y);
      var dif_close = Math.abs(close - y);
      if (dif_open <= dif_high && dif_open <= dif_low && dif_open <= dif_close) {
          if (dif_open < 6)
              result = pData.open;
      }
      if (dif_high <= dif_open && dif_high <= dif_low && dif_high <= dif_close) {
          if (dif_high < 6)
              result = pData.high;
      }
      if (dif_low <= dif_open && dif_low <= dif_high && dif_low <= dif_close) {
          if (dif_low < 6)
              result = pData.low;
      }
      if (dif_close <= dif_open && dif_close <= dif_high && dif_close <= dif_low) {
          if (dif_close < 6)
              result = pData.close;
      }
      return result;
  };
  var CToolObject = create_class(NamedObject);
  CToolObject.state = {
      BeforeDraw: 0,
      Draw: 1,
      AfterDraw: 2
  };
  CToolObject.prototype.__construct = function (name) {
      CToolObject.__super.__construct.call(this, name);
      this.drawer = null;
      this.state = CToolObject.state.BeforeDraw;
      this.points = [];
      this.step = 0;
  };
  CToolObject.prototype.getChartObjects = function () {
      var ppMgr = ChartManager.getInstance();
      var ppCDS = ppMgr.getDataSource("frame0.k0");
      if (ppCDS == null || !is_instance(ppCDS, MainDataSource))
          return null;
      var ppTimeline = ppMgr.getTimeline("frame0.k0");
      if (ppTimeline == null)
          return null;
      var ppArea = ppMgr.getArea('frame0.k0.main');
      if (ppArea == null)
          return null;
      var ppRange = ppMgr.getRange("frame0.k0.main");
      if (ppRange == null)
          return null;
      return {pMgr: ppMgr, pCDS: ppCDS, pTimeline: ppTimeline, pArea: ppArea, pRange: ppRange};
  };
  CToolObject.prototype.isValidMouseXY = function (x, y) {
      var pObj = this.getChartObjects();
      var areaPos = {
          left: pObj.pArea.getLeft(),
          top: pObj.pArea.getTop(),
          right: pObj.pArea.getRight(),
          bottom: pObj.pArea.getBottom()
      };
      if (x < areaPos.left || x > areaPos.right ||
          y < areaPos.top || y > areaPos.bottom)
          return false;
      return true;
  };
  CToolObject.prototype.getPlotter = function () {
      return this.drawer;
  };
  CToolObject.prototype.setState = function (s) {
      this.state = s;
  };
  CToolObject.prototype.getState = function () {
      return this.state;
  };
  CToolObject.prototype.addPoint = function (point) {
      this.points.push(point);
  };
  CToolObject.prototype.getPoint = function (i) {
      return this.points[i];
  };
  CToolObject.prototype.acceptMouseMoveEvent = function (x, y) {
      if (this.isValidMouseXY(x, y) == false)
          return false;
      if (this.state == CToolObject.state.BeforeDraw) {
          this.setBeforeDrawPos(x, y);
      } else if (this.state == CToolObject.state.Draw) {
          this.setDrawPos(x, y);
      } else if (this.state == CToolObject.state.AfterDraw) {
          this.setAfterDrawPos(x, y);
      }
      return true;
  };
  CToolObject.prototype.acceptMouseDownEvent = function (x, y) {
      if (this.isValidMouseXY(x, y) == false)
          return false;
      if (this.state == CToolObject.state.BeforeDraw) {
          this.setDrawPos(x, y);
          this.setState(CToolObject.state.Draw);
      } else if (this.state == CToolObject.state.Draw) {
          this.setAfterDrawPos(x, y);
          if (this.step == 0)
              this.setState(CToolObject.state.AfterDraw);
      } else if (this.state == CToolObject.state.AfterDraw) {
          if (CToolObject.prototype.isSelected.call(this, x, y)) {
              this.setDrawPos(x, y);
              this.setState(CToolObject.state.Draw);
          } else {
              this.oldx = x;
              this.oldy = y;
          }
      }
      return true;
  };
  CToolObject.prototype.acceptMouseDownMoveEvent = function (x, y) {
      if (this.isValidMouseXY(x, y) == false)
          return false;
      if (this.state == CToolObject.state.Draw) {
          this.setDrawPos(x, y);
      } else if (this.state == CToolObject.state.AfterDraw) {
          var pObj = this.getChartObjects();
          var _width = pObj.pTimeline.getItemWidth();
          var _height = pObj.pRange;
          if (Math.abs(x - this.oldx) < _width && Math.abs(y - this.oldy) == 0)
              return true;
          var _old_x = pObj.pTimeline.toIndex(this.oldx);
          var _old_y = pObj.pRange.toValue(this.oldy);
          var _new_x = pObj.pTimeline.toIndex(x);
          var _new_y = pObj.pRange.toValue(y);
          this.oldx = x;
          this.oldy = y;
          var _dif_x = _new_x - _old_x;
          var _dif_y = _new_y - _old_y;
          for (var index in this.points) {
              this.points[index].pos.index += _dif_x;
              this.points[index].pos.value += _dif_y;
          }
      }
      return true;
  };
  CToolObject.prototype.acceptMouseUpEvent = function (x, y) {
      if (this.isValidMouseXY(x, y) == false)
          return false;
      if (this.state == CToolObject.state.Draw) {
          this.setAfterDrawPos(x, y);
          if (this.step == 0)
              this.setState(CToolObject.state.AfterDraw);
          return true;
      }
      return false;
  };
  CToolObject.prototype.setBeforeDrawPos = function (x, y) {
      for (var index in this.points) {
          this.points[index].setPosXY(x, y);
          this.points[index].setState(CPoint.state.Show);
      }
  };
  CToolObject.prototype.setDrawPos = function (x, y) {
      for (var index in this.points) {
          if (this.points[index].getState() == CPoint.state.Highlight) {
              this.points[index].setPosXY(x, y);
          }
      }
  };
  CToolObject.prototype.setAfterDrawPos = function (x, y) {
      if (this.step != 0)
          this.step -= 1;
      for (var index in this.points) {
          this.points[index].setState(CPoint.state.Hide);
      }
      if (this.step == 0) {
          var pObj = this.getChartObjects();
          pObj.pMgr.setNormalMode();
      }
  };
  CToolObject.prototype.isSelected = function (x, y) {
      var isFind = false;
      for (var index in this.points) {
          if (this.points[index].isSelected(x, y)) {
              this.points[index].setState(CPoint.state.Highlight);
              isFind = true;
              break;
          }
      }
      if (isFind == true) {
          this.select();
          return true;
      }
      return false;
  };
  CToolObject.prototype.select = function () {
      for (var index in this.points) {
          if (this.points[index].getState() == CPoint.state.Hide) {
              this.points[index].setState(CPoint.state.Show);
          }
      }
  };
  CToolObject.prototype.unselect = function () {
      for (var index in this.points) {
          if (this.points[index].getState() != CPoint.state.Hide) {
              this.points[index].setState(CPoint.state.Hide);
          }
      }
  };
  CToolObject.prototype.calcDistance = function (point1, point2, point3) {
      var xa = point1.getPosXY().x;
      var ya = point1.getPosXY().y;
      var xb = point2.getPosXY().x;
      var yb = point2.getPosXY().y;
      var xc = point3.getPosXY().x;
      var yc = point3.getPosXY().y;
      var a1 = xa - xc;
      var a2 = ya - yc;
      var b1 = xb - xc;
      var b2 = yb - yc;
      var area = Math.abs(a1 * b2 - a2 * b1);
      var len = Math.sqrt(Math.pow((xb - xa), 2) + Math.pow((yb - ya), 2));
      return area / len;
  };
  CToolObject.prototype.calcGap = function (r, x, y) {
      var xa = r.sx;
      var ya = r.sy;
      var xb = r.ex;
      var yb = r.ey;
      var xc = x;
      var yc = y;
      var a1 = xa - xc;
      var a2 = ya - yc;
      var b1 = xb - xc;
      var b2 = yb - yc;
      var area = Math.abs(a1 * b2 - a2 * b1);
      var len = Math.sqrt(Math.pow((xb - xa), 2) + Math.pow((yb - ya), 2));
      return area / len;
  };
  CToolObject.prototype.isWithRect = function (point1, point2, point3) {
      var sx = point1.getPosXY().x;
      var sy = point1.getPosXY().y;
      var ex = point2.getPosXY().x;
      var ey = point2.getPosXY().y;
      var x = point3.getPosXY().x;
      var y = point3.getPosXY().y;
      if (sx > ex) {
          sx += 4;
          ex -= 4;
      } else {
          sx -= 4;
          ex += 4;
      }
      if (sy > ey) {
          sy += 4;
          ey -= 4;
      } else {
          sy -= 4;
          ey += 4;
      }
      if (sx <= x && ex >= x && sy <= y && ey >= y)
          return true;
      if (sx >= x && ex <= x && sy <= y && ey >= y)
          return true;
      if (sx <= x && ex >= x && sy >= y && ey <= y)
          return true;
      if (sx >= x && ex <= x && sy >= y && ey <= y)
          return true;
      return false;
  };
  var CBiToolObject = create_class(CToolObject);
  CBiToolObject.prototype.__construct = function (name) {
      CBiToolObject.__super.__construct.call(this, name);
      this.addPoint(new CPoint(name));
      this.addPoint(new CPoint(name));
  };
  CBiToolObject.prototype.setBeforeDrawPos = function (x, y) {
      this.step = 1;
      CBiToolObject.__super.setBeforeDrawPos.call(this, x, y);
      this.getPoint(0).setState(CPoint.state.Show);
      this.getPoint(1).setState(CPoint.state.Highlight);
  };
  var CTriToolObject = create_class(CToolObject);
  CTriToolObject.prototype.__construct = function (name) {
      CTriToolObject.__super.__construct.call(this, name);
      this.addPoint(new CPoint(name));
      this.addPoint(new CPoint(name));
      this.addPoint(new CPoint(name));
  };
  CTriToolObject.prototype.setBeforeDrawPos = function (x, y) {
      this.step = 2;
      CBiToolObject.__super.setBeforeDrawPos.call(this, x, y);
      this.getPoint(0).setState(CPoint.state.Show);
      this.getPoint(1).setState(CPoint.state.Show);
      this.getPoint(2).setState(CPoint.state.Highlight);
  };
  CTriToolObject.prototype.setAfterDrawPos = function (x, y) {
      if (this.step != 0)
          this.step -= 1;
      if (this.step == 0) {
          for (var index in this.points) {
              this.points[index].setState(CPoint.state.Hide);
          }
      } else {
          this.getPoint(0).setState(CPoint.state.Show);
          this.getPoint(1).setState(CPoint.state.Highlight);
          this.getPoint(2).setState(CPoint.state.Show);
      }
      if (this.step == 0) {
          var pObj = this.getChartObjects();
          pObj.pMgr.setNormalMode();
      }
  };
  var CBandLineObject = create_class(CBiToolObject);
  CBandLineObject.prototype.__construct = function (name) {
      CBandLineObject.__super.__construct.call(this, name);
      this.drawer = new DrawBandLinesPlotter(name, this);
  };
  CBandLineObject.prototype.isSelected = function (x, y) {
      if (CBandLineObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var c = new CPoint("frame0.k0");
      c.setPosXY(x, y);
      var sx = this.getPoint(0).getPosXY().x;
      var sy = this.getPoint(0).getPosXY().y;
      var ex = this.getPoint(1).getPosXY().x;
      var ey = this.getPoint(1).getPosXY().y;
      var fibSequence = [100.0, 87.5, 75.0, 62.5, 50.0, 37.5, 25.0, 12.5, 0.0];
      for (var i = 0; i < fibSequence.length; i++) {
          var stage_y = sy + (100 - fibSequence[i]) / 100 * (ey - sy);
          if (stage_y < y + 4 && stage_y > y - 4) {
              this.select();
              return true;
          }
      }
      return false;
  };
  var CBiParallelLineObject = create_class(CTriToolObject);
  CBiParallelLineObject.prototype.__construct = function (name) {
      CBiParallelLineObject.__super.__construct.call(this, name);
      this.drawer = new DrawBiParallelLinesPlotter(name, this);
  };
  CBiParallelLineObject.prototype.isSelected = function (x, y) {
      if (CTriParallelLineObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var _0x = this.getPoint(0).getPosXY().x;
      var _0y = this.getPoint(0).getPosXY().y;
      var _1x = this.getPoint(1).getPosXY().x;
      var _1y = this.getPoint(1).getPosXY().y;
      var _2x = this.getPoint(2).getPosXY().x;
      var _2y = this.getPoint(2).getPosXY().y;
      var _a = {x: _0x - _1x, y: _0y - _1y};
      var _b = {x: _0x - _2x, y: _0y - _2y};
      var _c = {x: _a.x + _b.x, y: _a.y + _b.y};
      var _3x = _0x - _c.x;
      var _3y = _0y - _c.y;
      var r1 = {sx: _0x, sy: _0y, ex: _2x, ey: _2y};
      var r2 = {sx: _1x, sy: _1y, ex: _3x, ey: _3y};
      if (this.calcGap(r1, x, y) > 4 && this.calcGap(r2, x, y) > 4)
          return false;
      return true;
  };
  var CBiParallelRayLineObject = create_class(CTriToolObject);
  CBiParallelRayLineObject.prototype.__construct = function (name) {
      CBiParallelRayLineObject.__super.__construct.call(this, name);
      this.drawer = new DrawBiParallelRayLinesPlotter(name, this);
  };
  CBiParallelRayLineObject.prototype.isSelected = function (x, y) {
      if (CTriParallelLineObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var _0x = this.getPoint(0).getPosXY().x;
      var _0y = this.getPoint(0).getPosXY().y;
      var _1x = this.getPoint(1).getPosXY().x;
      var _1y = this.getPoint(1).getPosXY().y;
      var _2x = this.getPoint(2).getPosXY().x;
      var _2y = this.getPoint(2).getPosXY().y;
      var _a = {x: _0x - _1x, y: _0y - _1y};
      var _b = {x: _0x - _2x, y: _0y - _2y};
      var _c = {x: _a.x + _b.x, y: _a.y + _b.y};
      var _3x = _0x - _c.x;
      var _3y = _0y - _c.y;
      var r1 = {sx: _0x, sy: _0y, ex: _2x, ey: _2y};
      var r2 = {sx: _1x, sy: _1y, ex: _3x, ey: _3y};
      if ((r1.ex > r1.sx && x > r1.sx - 4) || (r1.ex < r1.sx && x < r1.sx + 4) ||
          (r2.ex > r2.sx && x > r2.sx - 4) || (r2.ex < r2.sx && x < r2.sx + 4)) {
          if (this.calcGap(r1, x, y) > 4 && this.calcGap(r2, x, y) > 4) {
              return false;
          }
      } else {
          return false;
      }
      this.select();
      return true;
  };
  var CFibFansObject = create_class(CBiToolObject);
  CFibFansObject.prototype.__construct = function (name) {
      CFibFansObject.__super.__construct.call(this, name);
      this.drawer = new DrawFibFansPlotter(name, this);
  };
  CFibFansObject.prototype.isSelected = function (x, y) {
      if (CFibFansObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var c = new CPoint("frame0.k0");
      c.setPosXY(x, y);
      var sx = this.getPoint(0).getPosXY().x;
      var sy = this.getPoint(0).getPosXY().y;
      var ex = this.getPoint(1).getPosXY().x;
      var ey = this.getPoint(1).getPosXY().y;
      var pObj = this.getChartObjects();
      var areaPos = {
          left: pObj.pArea.getLeft(),
          top: pObj.pArea.getTop(),
          right: pObj.pArea.getRight(),
          bottom: pObj.pArea.getBottom()
      };
      var fibFansSequence = [0, 38.2, 50, 61.8];
      for (var i = 0; i < fibFansSequence.length; i++) {
          var stageY = sy + (100 - fibFansSequence[i]) / 100 * (ey - sy);
          var tempStartPt = {x: sx, y: sy};
          var tempEndPt = {x: ex, y: stageY};
          var crossPt = getRectCrossPt(areaPos, tempStartPt, tempEndPt);
          var lenToStartPt = Math.pow((crossPt[0].x - sx), 2) + Math.pow((crossPt[0].y - sy), 2);
          var lenToEndPt = Math.pow((crossPt[0].x - ex), 2) + Math.pow((crossPt[0].y - ey), 2);
          var tempCrossPt = lenToStartPt > lenToEndPt ? {x: crossPt[0].x, y: crossPt[0].y} : {
              x: crossPt[1].x,
              y: crossPt[1].y
          };
          if (tempCrossPt.x > sx && x < sx)
              continue;
          if (tempCrossPt.x < sx && x > sx)
              continue;
          var a = new CPoint("frame0.k0");
          a.setPosXY(sx, sy);
          var b = new CPoint("frame0.k0");
          b.setPosXY(tempCrossPt.x, tempCrossPt.y);
          if (this.calcDistance(a, b, c) > 4)
              continue;
          this.select();
          return true;
      }
      return false;
  };
  var CFibRetraceObject = create_class(CBiToolObject);
  CFibRetraceObject.prototype.__construct = function (name) {
      CFibRetraceObject.__super.__construct.call(this, name);
      this.drawer = new DrawFibRetracePlotter(name, this);
  };
  CFibRetraceObject.prototype.isSelected = function (x, y) {
      if (CFibRetraceObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var c = new CPoint("frame0.k0");
      c.setPosXY(x, y);
      var sx = this.getPoint(0).getPosXY().x;
      var sy = this.getPoint(0).getPosXY().y;
      var ex = this.getPoint(1).getPosXY().x;
      var ey = this.getPoint(1).getPosXY().y;
      var fibSequence = [100.0, 78.6, 61.8, 50.0, 38.2, 23.6, 0.0];
      for (var i = 0; i < fibSequence.length; i++) {
          var stage_y = sy + (100 - fibSequence[i]) / 100 * (ey - sy);
          if (stage_y < y + 4 && stage_y > y - 4) {
              this.select();
              return true;
          }
      }
      return false;
  };
  var CHoriRayLineObject = create_class(CBiToolObject);
  CHoriRayLineObject.prototype.__construct = function (name) {
      CHoriRayLineObject.__super.__construct.call(this, name);
      this.drawer = new DrawHoriRayLinesPlotter(name, this);
  };
  CHoriRayLineObject.prototype.setDrawPos = function (x, y) {
      if (this.points[0].getState() == CPoint.state.Highlight) {
          this.points[0].setPosXY(x, y);
          this.points[1].setPosXYNoSnap(this.points[1].getPosXY().x, this.points[0].getPosXY().y);
          return;
      }
      if (this.points[1].getState() == CPoint.state.Highlight) {
          this.points[1].setPosXY(x, y);
          this.points[0].setPosXYNoSnap(this.points[0].getPosXY().x, this.points[1].getPosXY().y);
      }
  };
  CHoriRayLineObject.prototype.isSelected = function (x, y) {
      if (CHoriRayLineObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var c = new CPoint("frame0.k0");
      c.setPosXY(x, y);
      var sy = this.getPoint(0).getPosXY().y;
      var sx = this.getPoint(0).getPosXY().x;
      var ex = this.getPoint(1).getPosXY().x;
      if (y > sy + 4 || y < sy - 4)
          return false;
      if (ex > sx && x < sx - 4)
          return false;
      if (ex < sx && x > sx + 4)
          return false;
      this.select();
      return true;
  };
  var CHoriSegLineObject = create_class(CBiToolObject);
  CHoriSegLineObject.prototype.__construct = function (name) {
      CHoriSegLineObject.__super.__construct.call(this, name);
      this.drawer = new DrawHoriSegLinesPlotter(name, this);
  };
  CHoriSegLineObject.prototype.setDrawPos = function (x, y) {
      if (this.points[0].getState() == CPoint.state.Highlight) {
          this.points[0].setPosXY(x, y);
          this.points[1].setPosXYNoSnap(this.points[1].getPosXY().x, this.points[0].getPosXY().y);
          return;
      }
      if (this.points[1].getState() == CPoint.state.Highlight) {
          this.points[1].setPosXY(x, y);
          this.points[0].setPosXYNoSnap(this.points[0].getPosXY().x, this.points[1].getPosXY().y);
      }
  };
  CHoriSegLineObject.prototype.isSelected = function (x, y) {
      if (CHoriSegLineObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var c = new CPoint("frame0.k0");
      c.setPosXY(x, y);
      var sy = this.getPoint(0).getPosXY().y;
      var sx = this.getPoint(0).getPosXY().x;
      var ex = this.getPoint(1).getPosXY().x;
      if (y > sy + 4 || y < sy - 4)
          return false;
      if (sx > ex && (x > sx + 4 || x < ex - 4))
          return false;
      if (sx < ex && (x < sx - 4 || x > ex + 4))
          return false;
      this.select();
      return true;
  };
  var CHoriStraightLineObject = create_class(CBiToolObject);
  CHoriStraightLineObject.prototype.__construct = function (name) {
      CHoriStraightLineObject.__super.__construct.call(this, name);
      this.drawer = new DrawHoriStraightLinesPlotter(name, this);
  };
  CHoriStraightLineObject.prototype.setDrawPos = function (x, y) {
      for (var index in this.points) {
          this.points[index].setPosXY(x, y);
      }
  };
  CHoriStraightLineObject.prototype.isSelected = function (x, y) {
      if (CHoriStraightLineObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var c = new CPoint("frame0.k0");
      c.setPosXY(x, y);
      var sy = this.getPoint(0).getPosXY().y;
      if (y > sy + 4 || y < sy - 4)
          return false;
      this.select();
      return true;
  };
  var CRayLineObject = create_class(CBiToolObject);
  CRayLineObject.prototype.__construct = function (name) {
      CRayLineObject.__super.__construct.call(this, name);
      this.drawer = new DrawRayLinesPlotter(name, this);
  };
  CRayLineObject.prototype.isSelected = function (x, y) {
      if (CRayLineObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var c = new CPoint("frame0.k0");
      c.setPosXY(x, y);
      var sx = this.getPoint(0).getPosXY().x;
      var ex = this.getPoint(1).getPosXY().x;
      if (ex > sx && x < sx - 4)
          return false;
      if (ex < sx && x > sx + 4)
          return false;
      if (this.calcDistance(this.getPoint(0), this.getPoint(1), c) < 4) {
          this.select();
          return true;
      }
      return false;
  };
  var CSegLineObject = create_class(CBiToolObject);
  CSegLineObject.prototype.__construct = function (name) {
      CSegLineObject.__super.__construct.call(this, name);
      this.drawer = new DrawSegLinesPlotter(name, this);
  };
  CSegLineObject.prototype.isSelected = function (x, y) {
      if (CSegLineObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var c = new CPoint("frame0.k0");
      c.setPosXY(x, y);
      if (this.isWithRect(this.getPoint(0), this.getPoint(1), c) == false)
          return false;
      if (this.calcDistance(this.getPoint(0), this.getPoint(1), c) < 4) {
          this.select();
          return true;
      }
      return false;
  };
  var CStraightLineObject = create_class(CBiToolObject);
  CStraightLineObject.prototype.__construct = function (name) {
      CStraightLineObject.__super.__construct.call(this, name);
      this.drawer = new DrawStraightLinesPlotter(name, this);
  };
  CStraightLineObject.prototype.isSelected = function (x, y) {
      if (CStraightLineObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var c = new CPoint("frame0.k0");
      c.setPosXY(x, y);
      if (this.calcDistance(this.getPoint(0), this.getPoint(1), c) < 4) {
          this.select();
          return true;
      }
      return false;
  };
  var CTriParallelLineObject = create_class(CTriToolObject);
  CTriParallelLineObject.prototype.__construct = function (name) {
      CTriParallelLineObject.__super.__construct.call(this, name);
      this.drawer = new DrawTriParallelLinesPlotter(name, this);
  };
  CTriParallelLineObject.prototype.isSelected = function (x, y) {
      if (CTriParallelLineObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var pObj = this.getChartObjects();
      var _0x = this.getPoint(0).getPosXY().x;
      var _0y = this.getPoint(0).getPosXY().y;
      var _1x = this.getPoint(1).getPosXY().x;
      var _1y = this.getPoint(1).getPosXY().y;
      var _2x = this.getPoint(2).getPosXY().x;
      var _2y = this.getPoint(2).getPosXY().y;
      var _a = {x: _0x - _1x, y: _0y - _1y};
      var _b = {x: _0x - _2x, y: _0y - _2y};
      var _c = {x: _a.x + _b.x, y: _a.y + _b.y};
      var _3x = _0x - _c.x;
      var _3y = _0y - _c.y;
      var r1 = {sx: _0x, sy: _0y, ex: _2x, ey: _2y};
      var r2 = {sx: _1x, sy: _1y, ex: _3x, ey: _3y};
      var _i = {x: _0x - _1x, y: _0y - _1y};
      var _j = {x: _2x - _3x, y: _2y - _3y};
      var _ri = {x: _1x - _0x, y: _1y - _0y};
      var _rj = {x: _3x - _2x, y: _3y - _2y};
      var _4x = Math.abs(_ri.x - _0x);
      var _4y = Math.abs(_ri.y - _0y);
      var _5x = Math.abs(_rj.x - _2x);
      var _5y = Math.abs(_rj.y - _2y);
      var r3 = {sx: _4x, sy: _4y, ex: _5x, ey: _5y};
      if (this.calcGap(r1, x, y) > 4 &&
          this.calcGap(r2, x, y) > 4 &&
          this.calcGap(r3, x, y) > 4)
          return false;
      this.select();
      return true;
  };
  var CVertiStraightLineObject = create_class(CBiToolObject);
  CVertiStraightLineObject.prototype.__construct = function (name) {
      CVertiStraightLineObject.__super.__construct.call(this, name);
      this.drawer = new DrawVertiStraightLinesPlotter(name, this);
  };
  CVertiStraightLineObject.prototype.setDrawPos = function (x, y) {
      for (var index in this.points) {
          this.points[index].setPosXY(x, y);
      }
  };
  CVertiStraightLineObject.prototype.isSelected = function (x, y) {
      if (CVertiStraightLineObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var c = new CPoint("frame0.k0");
      c.setPosXY(x, y);
      var sx = this.getPoint(0).getPosXY().x;
      if (x > sx + 4 || x < sx - 4)
          return false;
      this.select();
      return true;
  };
  var CPriceLineObject = create_class(CSegLineObject);
  CPriceLineObject.prototype.__construct = function (name) {
      CPriceLineObject.__super.__construct.call(this, name);
      this.drawer = new DrawPriceLinesPlotter(name, this);
  };
  CPriceLineObject.prototype.setDrawPos = function (x, y) {
      for (var index in this.points) {
          this.points[index].setPosXY(x, y);
      }
  };
  CPriceLineObject.prototype.isSelected = function (x, y) {
      if (CFibRetraceObject.__super.isSelected.call(this, x, y) == true)
          return true;
      var c = new CPoint("frame0.k0");
      c.setPosXY(x, y);
      var sx = this.getPoint(0).getPosXY().x;
      var sy = this.getPoint(0).getPosXY().y;
      var ex = this.getPoint(1).getPosXY().x;
      var ey = this.getPoint(1).getPosXY().y;
      if (x < sx - 4)
          return false;
      if (y >= sy + 4 || y <= sy - 4)
          return false;
      this.select();
      return true;
  };
  var CArrowLineObject = create_class(CSegLineObject);
  CArrowLineObject.prototype.__construct = function (name) {
      CArrowLineObject.__super.__construct.call(this, name);
      this.drawer = new DrawArrowLinesPlotter(name, this);
  };
  var CToolManager = create_class(NamedObject);
  CToolManager.prototype.__construct = function (name) {
      CToolManager.__super.__construct.call(this, name);
      this.selectedObject = -1;
      this.toolObjects = [];
  };
  CToolManager.prototype.getToolObjectCount = function () {
      return this.toolObjects.length;
  };
  CToolManager.prototype.addToolObject = function (o) {
      this.toolObjects.push(o);
  };
  CToolManager.prototype.getToolObject = function (i) {
      if (i < this.toolObjects.length && i >= 0)
          return this.toolObjects[i];
      return null;
  };
  CToolManager.prototype.getCurrentObject = function () {
      return this.getToolObject(this.getToolObjectCount() - 1);
  };
  CToolManager.prototype.getSelectedObject = function () {
      return this.getToolObject(this.selectedObject);
  };
  CToolManager.prototype.delCurrentObject = function () {
      this.toolObjects.splice(this.getToolObjectCount() - 1, 1);
  };
  CToolManager.prototype.delSelectedObject = function () {
      this.toolObjects.splice(this.selectedObject, 1);
      this.selectedObject = -1;
  };
  CToolManager.prototype.acceptMouseMoveEvent = function (x, y) {
      if (this.selectedObject == -1) {
          var curr = this.toolObjects[this.getToolObjectCount() - 1];
          if (curr != null && curr.getState() != CToolObject.state.AfterDraw)
              return curr.acceptMouseMoveEvent(x, y);
      } else {
          var sel = this.toolObjects[this.selectedObject];
          if (sel.getState() == CToolObject.state.Draw)
              return sel.acceptMouseMoveEvent(x, y);
          sel.unselect();
          this.selectedObject = -1;
      }
      for (var index in this.toolObjects) {
          if (this.toolObjects[index].isSelected(x, y)) {
              this.selectedObject = index;
              return false;
          }
      }
      return false;
  };
  CToolManager.prototype.acceptMouseDownEvent = function (x, y) {
      this.mouseDownMove = false;
      if (this.selectedObject == -1) {
          var curr = this.toolObjects[this.getToolObjectCount() - 1];
          if (curr != null && curr.getState() != CToolObject.state.AfterDraw)
              return curr.acceptMouseDownEvent(x, y);
      } else {
          var sel = this.toolObjects[this.selectedObject];
          if (sel.getState() != CToolObject.state.BeforeDraw)
              return sel.acceptMouseDownEvent(x, y);
      }
      return false;
  };
  CToolManager.prototype.acceptMouseDownMoveEvent = function (x, y) {
      this.mouseDownMove = true;
      if (this.selectedObject == -1) {
          var curr = this.toolObjects[this.getToolObjectCount() - 1];
          if (curr != null && curr.getState() == CToolObject.state.Draw)
              return curr.acceptMouseDownMoveEvent(x, y);
          return false;
      } else {
          var sel = this.toolObjects[this.selectedObject];
          if (sel.getState() != CToolObject.state.BeforeDraw) {
              if (sel.acceptMouseDownMoveEvent(x, y) == true) {
                  var point = this.toolObjects[this.selectedObject].points;
                  for (var i = 0; i < point.length; i++) {
                      if (point[i].state == CPoint.state.Highlight || point[i].state == CPoint.state.Show) {
                          return true;
                      }
                  }
              }
              return true;
          }
      }
  };
  CToolManager.prototype.acceptMouseUpEvent = function (x, y) {
      if (this.mouseDownMove == true) {
          if (this.selectedObject == -1) {
              var curr = this.toolObjects[this.getToolObjectCount() - 1];
              if (curr != null && curr.getState() == CToolObject.state.Draw)
                  return curr.acceptMouseUpEvent(x, y);
              return true;
          } else {
              var sel = this.toolObjects[this.selectedObject];
              if (sel.getState() != CToolObject.state.BeforeDraw)
                  return sel.acceptMouseUpEvent(x, y);
          }
      }
      if (this.selectedObject != -1) {
          return true;
      }
      var curr = this.toolObjects[this.getToolObjectCount() - 1];
      if (curr != null) {
          if (curr.getState() == CToolObject.state.Draw)
              return true;
          if (!curr.isValidMouseXY(x, y)) {
              return false;
          }
          if (curr.isSelected(x, y)) {
              return true;
          }
      }
      return false;
  };
  var CToolPlotter = create_class(NamedObject);
  CToolPlotter.prototype.__construct = function (name, toolObject) {
      CToolPlotter.__super.__construct.call(this, name);
      this.toolObject = toolObject;
      var pMgr = ChartManager.getInstance();
      var pArea = pMgr.getArea('frame0.k0.main');
      if (pArea == null) {
          this.areaPos = {
              left: 0,
              top: 0,
              right: 0,
              bottom: 0
          };
          return;
      }
      this.areaPos = {
          left: pArea.getLeft(),
          top: pArea.getTop(),
          right: pArea.getRight(),
          bottom: pArea.getBottom()
      };
      this.crossPt = {};
      this.normalSize = 4;
      this.selectedSize = 6;
      this.cursorLen = 4;
      this.cursorGapLen = 3;
      this.theme = ChartManager.getInstance().getTheme(this.getFrameName());
  };
  CToolPlotter.prototype.drawCursor = function (context) {
      this.drawCrossCursor(context);
  };
  CToolPlotter.prototype.drawCrossCursor = function (context) {
      context.strokeStyle = this.theme.getColor(Theme.Color.LineColorNormal);
      context.fillStyle = this.theme.getColor(Theme.Color.LineColorNormal);
      var tempPt = this.toolObject.getPoint(0).getPosXY();
      if (tempPt == null) {
          return;
      }
      var x = tempPt.x;
      var y = tempPt.y;
      var cursorLen = this.cursorLen;
      var cursorGapLen = this.cursorGapLen;
      context.fillRect(x, y, 1, 1);
      Plotter.drawLine(context, x - cursorLen - cursorGapLen, y, x - cursorGapLen, y);
      Plotter.drawLine(context, x + cursorLen + cursorGapLen, y, x + cursorGapLen, y);
      Plotter.drawLine(context, x, y - cursorLen - cursorGapLen, x, y - cursorGapLen);
      Plotter.drawLine(context, x, y + cursorLen + cursorGapLen, x, y + cursorGapLen);
  };
  CToolPlotter.prototype.drawCircle = function (context, center, radius) {
      var centerX = center.x;
      var centerY = center.y;
      context.beginPath();
      context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
      context.fillStyle = this.theme.getColor(Theme.Color.CircleColorFill);
      context.fill();
      context.stroke();
  };
  CToolPlotter.prototype.drawCtrlPt = function (context) {
      context.strokeStyle = this.theme.getColor(Theme.Color.CircleColorStroke);
      for (var i = 0; i < this.ctrlPtsNum; i++) {
          this.drawCircle(context, this.ctrlPts[1][i], this.normalSize);
      }
  };
  CToolPlotter.prototype.highlightCtrlPt = function (context) {
      context.strokeStyle = this.theme.getColor(Theme.Color.CircleColorStroke);
      for (var i = 0; i < this.ctrlPtsNum; i++) {
          if (this.toolObject.getPoint(i).getState() == CPoint.state.Highlight)
              this.drawCircle(context, this.ctrlPts[1][i], this.selectedSize);
      }
  };
  CToolPlotter.prototype.drawFibRayLines = function (context, startPoint, endPoint) {
      for (var i = 0; i < this.fiboFansSequence.length; i++) {
          var stageY = startPoint.y + (100 - this.fiboFansSequence[i]) / 100 * (endPoint.y - startPoint.y);
          var tempStartPt = {x: startPoint.x, y: startPoint.y};
          var tempEndPt = {x: endPoint.x, y: stageY};
          this.drawRayLines(context, tempStartPt, tempEndPt);
      }
  };
  CToolPlotter.prototype.drawRayLines = function (context, startPoint, endPoint) {
      this.getAreaPos();
      var tempStartPt = {x: startPoint.x, y: startPoint.y};
      var tempEndPt = {x: endPoint.x, y: endPoint.y};
      var crossPt = getRectCrossPt(this.areaPos, tempStartPt, tempEndPt);
      var tempCrossPt;
      if (endPoint.x == startPoint.x) {
          if (endPoint.y == startPoint.y) {
              tempCrossPt = endPoint;
          } else {
              tempCrossPt = endPoint.y > startPoint.y ? {x: crossPt[1].x, y: crossPt[1].y} : {
                  x: crossPt[0].x,
                  y: crossPt[0].y
              };
          }
      } else {
          tempCrossPt = endPoint.x > startPoint.x ? {x: crossPt[1].x, y: crossPt[1].y} : {
              x: crossPt[0].x,
              y: crossPt[0].y
          };
      }
      Plotter.drawLine(context, startPoint.x, startPoint.y, tempCrossPt.x, tempCrossPt.y);
  };
  CToolPlotter.prototype.lenBetweenPts = function (pt1, pt2) {
      return Math.sqrt(Math.pow((pt2.x - pt1.x), 2) + Math.pow((pt2.y - pt1.y), 2));
  };
  CToolPlotter.prototype.getCtrlPts = function () {
      for (var i = 0; i < this.ctrlPtsNum; i++) {
          this.ctrlPts[0][i] = this.toolObject.getPoint(i);
      }
  };
  CToolPlotter.prototype.updateCtrlPtPos = function () {
      for (var i = 0; i < this.ctrlPtsNum; i++) {
          this.ctrlPts[1][i] = this.ctrlPts[0][i].getPosXY();
      }
  };
  CToolPlotter.prototype.getAreaPos = function () {
      var pMgr = ChartManager.getInstance();
      var pArea = pMgr.getArea('frame0.k0.main');
      if (pArea == null) {
          this.areaPos = {
              left: 0,
              top: 0,
              right: 0,
              bottom: 0
          };
          return;
      }
      this.areaPos = {
          left: Math.floor(pArea.getLeft()),
          top: Math.floor(pArea.getTop()),
          right: Math.floor(pArea.getRight()),
          bottom: Math.floor(pArea.getBottom())
      };
  };
  CToolPlotter.prototype.updateDraw = function (context) {
      context.strokeStyle = this.theme.getColor(Theme.Color.LineColorNormal);
      this.draw(context);
      this.drawCtrlPt(context);
  };
  CToolPlotter.prototype.finishDraw = function (context) {
      context.strokeStyle = this.theme.getColor(Theme.Color.LineColorNormal);
      this.draw(context);
  };
  CToolPlotter.prototype.highlight = function (context) {
      context.strokeStyle = this.theme.getColor(Theme.Color.LineColorSelected);
      this.draw(context);
      this.drawCtrlPt(context);
      this.highlightCtrlPt(context);
  };
  var DrawStraightLinesPlotter = create_class(CToolPlotter);
  DrawStraightLinesPlotter.prototype.__construct = function (name, toolObject) {
      DrawStraightLinesPlotter.__super.__construct.call(this, name, toolObject);
      this.toolObject = toolObject;
      this.ctrlPtsNum = 2;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  DrawStraightLinesPlotter.prototype.draw = function (context) {
      this.updateCtrlPtPos();
      this.getAreaPos();
      this.startPoint = this.ctrlPts[1][0];
      this.endPoint = this.ctrlPts[1][1];
      if (this.startPoint.x == this.endPoint.x && this.startPoint.y == this.endPoint.y) {
          Plotter.drawLine(context, this.areaPos.left, this.startPoint.y, this.areaPos.right, this.startPoint.y);
      } else {
          this.crossPt = getRectCrossPt(this.areaPos, this.startPoint, this.endPoint);
          Plotter.drawLine(context, this.crossPt[0].x, this.crossPt[0].y, this.crossPt[1].x, this.crossPt[1].y);
      }
  };
  var DrawSegLinesPlotter = create_class(CToolPlotter);
  DrawSegLinesPlotter.prototype.__construct = function (name, toolObject) {
      DrawSegLinesPlotter.__super.__construct.call(this, name, toolObject);
      this.toolObject = toolObject;
      this.ctrlPtsNum = 2;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  DrawSegLinesPlotter.prototype.draw = function (context) {
      this.updateCtrlPtPos();
      this.startPoint = this.ctrlPts[1][0];
      this.endPoint = this.ctrlPts[1][1];
      if (this.startPoint.x == this.endPoint.x && this.startPoint.y == this.endPoint.y) {
          this.endPoint.x += 1;
      }
      Plotter.drawLine(context, this.startPoint.x, this.startPoint.y, this.endPoint.x, this.endPoint.y);
  };
  var DrawRayLinesPlotter = create_class(CToolPlotter);
  DrawRayLinesPlotter.prototype.__construct = function (name, toolObject) {
      DrawRayLinesPlotter.__super.__construct.call(this, name);
      this.toolObject = toolObject;
      this.ctrlPtsNum = 2;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  DrawRayLinesPlotter.prototype.draw = function (context) {
      this.updateCtrlPtPos();
      this.getAreaPos();
      this.startPoint = this.ctrlPts[1][0];
      this.endPoint = this.ctrlPts[1][1];
      if (this.startPoint.x == this.endPoint.x && this.startPoint.y == this.endPoint.y) {
          this.endPoint.x += 1;
      }
      this.drawRayLines(context, this.startPoint, this.endPoint);
  };
  var DrawArrowLinesPlotter = create_class(CToolPlotter);
  DrawArrowLinesPlotter.prototype.__construct = function (name, toolObject) {
      DrawArrowLinesPlotter.__super.__construct.call(this, name, toolObject);
      this.toolObject = toolObject;
      this.arrowSizeRatio = 0.03;
      this.arrowSize = 4;
      this.crossPt = {x: -1, y: -1};
      this.ctrlPtsNum = 2;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  DrawArrowLinesPlotter.prototype.drawArrow = function (context, startPoint, endPoint) {
      var len = this.lenBetweenPts(startPoint, endPoint);
      var vectorA = [endPoint.x - startPoint.x, endPoint.y - startPoint.y];
      this.crossPt.x = startPoint.x + (1 - this.arrowSize / len) * vectorA[0];
      this.crossPt.y = startPoint.y + (1 - this.arrowSize / len) * vectorA[1];
      var vectorAautho = [-vectorA[1], vectorA[0]];
      var Aautho = {x: vectorAautho[0], y: vectorAautho[1]};
      var origin = {x: 0, y: 0};
      vectorAautho[0] = this.arrowSize * Aautho.x / this.lenBetweenPts(Aautho, origin);
      vectorAautho[1] = this.arrowSize * Aautho.y / this.lenBetweenPts(Aautho, origin);
      var arrowEndPt = [this.crossPt.x + vectorAautho[0], this.crossPt.y + vectorAautho[1]];
      Plotter.drawLine(context, endPoint.x, endPoint.y, arrowEndPt[0], arrowEndPt[1]);
      arrowEndPt = [this.crossPt.x - vectorAautho[0], this.crossPt.y - vectorAautho[1]];
      Plotter.drawLine(context, endPoint.x, endPoint.y, arrowEndPt[0], arrowEndPt[1]);
  };
  DrawArrowLinesPlotter.prototype.draw = function (context) {
      this.updateCtrlPtPos();
      this.startPoint = this.ctrlPts[1][0];
      this.endPoint = this.ctrlPts[1][1];
      if (this.startPoint.x == this.endPoint.x && this.startPoint.y == this.endPoint.y) {
          this.endPoint.x += 1;
      }
      Plotter.drawLine(context, this.startPoint.x, this.startPoint.y, this.endPoint.x, this.endPoint.y);
      this.drawArrow(context, this.startPoint, this.endPoint);
  };
  var DrawHoriStraightLinesPlotter = create_class(CToolPlotter);
  DrawHoriStraightLinesPlotter.prototype.__construct = function (name, toolObject) {
      DrawHoriStraightLinesPlotter.__super.__construct.call(this, name);
      this.toolObject = toolObject;
      this.ctrlPtsNum = 1;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  DrawHoriStraightLinesPlotter.prototype.draw = function (context) {
      this.updateCtrlPtPos();
      this.getAreaPos();
      this.startPoint = this.ctrlPts[1][0];
      Plotter.drawLine(context, this.areaPos.left, this.startPoint.y, this.areaPos.right, this.startPoint.y);
  };
  var DrawHoriRayLinesPlotter = create_class(CToolPlotter);
  DrawHoriRayLinesPlotter.prototype.__construct = function (name, toolObject) {
      DrawHoriRayLinesPlotter.__super.__construct.call(this, name);
      this.toolObject = toolObject;
      this.ctrlPtsNum = 2;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  DrawHoriRayLinesPlotter.prototype.draw = function (context) {
      this.updateCtrlPtPos();
      this.getAreaPos();
      this.startPoint = this.ctrlPts[1][0];
      this.endPoint = this.ctrlPts[1][1];
      if (this.startPoint.x == this.endPoint.x) {
          Plotter.drawLine(context, this.startPoint.x, this.startPoint.y, this.areaPos.right, this.startPoint.y);
      } else {
          var tempEndPt = {x: this.endPoint.x, y: this.startPoint.y};
          this.drawRayLines(context, this.startPoint, tempEndPt);
      }
  };
  var DrawHoriSegLinesPlotter = create_class(CToolPlotter);
  DrawHoriSegLinesPlotter.prototype.__construct = function (name, toolObject) {
      DrawHoriSegLinesPlotter.__super.__construct.call(this, name, toolObject);
      this.toolObject = toolObject;
      this.ctrlPtsNum = 2;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  DrawHoriSegLinesPlotter.prototype.draw = function (context) {
      this.updateCtrlPtPos();
      this.startPoint = this.ctrlPts[1][0];
      this.endPoint = this.ctrlPts[1][1];
      this.endPoint.y = this.startPoint.y;
      if (this.startPoint.x == this.endPoint.x && this.startPoint.y == this.endPoint.y) {
          Plotter.drawLine(context, this.startPoint.x, this.startPoint.y, this.endPoint.x + 1, this.startPoint.y);
      } else {
          Plotter.drawLine(context, this.startPoint.x, this.startPoint.y, this.endPoint.x, this.startPoint.y);
      }
  };
  var DrawVertiStraightLinesPlotter = create_class(CToolPlotter);
  DrawVertiStraightLinesPlotter.prototype.__construct = function (name, toolObject) {
      DrawVertiStraightLinesPlotter.__super.__construct.call(this, name);
      this.toolObject = toolObject;
      this.ctrlPtsNum = 1;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  DrawVertiStraightLinesPlotter.prototype.draw = function (context) {
      this.updateCtrlPtPos();
      this.getAreaPos();
      this.startPoint = this.ctrlPts[1][0];
      Plotter.drawLine(context, this.startPoint.x, this.areaPos.top, this.startPoint.x, this.areaPos.bottom);
  };
  var DrawPriceLinesPlotter = create_class(CToolPlotter);
  DrawPriceLinesPlotter.prototype.__construct = function (name, toolObject) {
      DrawPriceLinesPlotter.__super.__construct.call(this, name);
      this.toolObject = toolObject;
      this.ctrlPtsNum = 1;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  DrawPriceLinesPlotter.prototype.draw = function (context) {
      context.font = "12px Tahoma";
      context.textAlign = "left";
      context.fillStyle = this.theme.getColor(Theme.Color.LineColorNormal);
      this.updateCtrlPtPos();
      this.getAreaPos();
      this.startPoint = this.ctrlPts[1][0];
      var text = this.ctrlPts[0][0].getPosIV().v;
      Plotter.drawLine(context, this.startPoint.x, this.startPoint.y, this.areaPos.right, this.startPoint.y);
      context.fillText(text.toFixed(2), this.startPoint.x + 2, this.startPoint.y - 15);
  };
  var ParallelLinesPlotter = create_class(CToolPlotter);
  ParallelLinesPlotter.prototype.__construct = function (name, toolObject) {
      ParallelLinesPlotter.__super.__construct.call(this, name);
      this.toolObject = toolObject;
  };
  ParallelLinesPlotter.prototype.getParaPt = function () {
      var vectorA = [];
      vectorA[0] = this.endPoint.x - this.startPoint.x;
      vectorA[1] = this.endPoint.y - this.startPoint.y;
      var vectorB = [];
      vectorB[0] = this.paraStartPoint.x - this.startPoint.x;
      vectorB[1] = this.paraStartPoint.y - this.startPoint.y;
      this.paraEndPoint = {x: -1, y: -1};
      this.paraEndPoint.x = vectorA[0] + vectorB[0] + this.startPoint.x;
      this.paraEndPoint.y = vectorA[1] + vectorB[1] + this.startPoint.y;
  };
  var DrawBiParallelLinesPlotter = create_class(ParallelLinesPlotter);
  DrawBiParallelLinesPlotter.prototype.__construct = function (name, toolObject) {
      DrawBiParallelLinesPlotter.__super.__construct.call(this, name, toolObject);
      this.toolObject = toolObject;
      this.ctrlPtsNum = 3;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  DrawBiParallelLinesPlotter.prototype.draw = function (context) {
      this.updateCtrlPtPos();
      this.getAreaPos();
      this.startPoint = this.ctrlPts[1][0];
      this.paraStartPoint = this.ctrlPts[1][1];
      this.endPoint = this.ctrlPts[1][2];
      this.getParaPt();
      this.getAreaPos();
      this.crossPt0 = getRectCrossPt(this.areaPos, this.startPoint, this.endPoint);
      Plotter.drawLine(context, this.crossPt0[0].x, this.crossPt0[0].y, this.crossPt0[1].x, this.crossPt0[1].y);
      this.crossPt1 = getRectCrossPt(this.areaPos, this.paraStartPoint, this.paraEndPoint);
      Plotter.drawLine(context, this.crossPt1[0].x, this.crossPt1[0].y, this.crossPt1[1].x, this.crossPt1[1].y);
  };
  var DrawBiParallelRayLinesPlotter = create_class(ParallelLinesPlotter);
  DrawBiParallelRayLinesPlotter.prototype.__construct = function (name, toolObject) {
      DrawBiParallelRayLinesPlotter.__super.__construct.call(this, name, toolObject);
      this.toolObject = toolObject;
      this.ctrlPtsNum = 3;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  DrawBiParallelRayLinesPlotter.prototype.draw = function (context) {
      this.updateCtrlPtPos();
      this.getAreaPos();
      this.startPoint = this.ctrlPts[1][0];
      this.paraStartPoint = this.ctrlPts[1][1];
      this.endPoint = this.ctrlPts[1][2];
      if (this.startPoint.x == this.endPoint.x && this.startPoint.y == this.endPoint.y) {
          this.endPoint.x += 1;
      }
      this.getParaPt();
      this.drawRayLines(context, this.startPoint, this.endPoint);
      this.drawRayLines(context, this.paraStartPoint, this.paraEndPoint);
  };
  var DrawTriParallelLinesPlotter = create_class(ParallelLinesPlotter);
  DrawTriParallelLinesPlotter.prototype.__construct = function (name, toolObject) {
      DrawTriParallelLinesPlotter.__super.__construct.call(this, name, toolObject);
      this.toolObject = toolObject;
      this.ctrlPtsNum = 3;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  DrawTriParallelLinesPlotter.prototype.draw = function (context) {
      this.updateCtrlPtPos();
      this.getAreaPos();
      this.startPoint = this.ctrlPts[1][0];
      this.paraStartPoint = this.ctrlPts[1][1];
      this.endPoint = this.ctrlPts[1][2];
      var vectorA = [];
      vectorA[0] = this.endPoint.x - this.startPoint.x;
      vectorA[1] = this.endPoint.y - this.startPoint.y;
      var vectorB = [];
      vectorB[0] = this.paraStartPoint.x - this.startPoint.x;
      vectorB[1] = this.paraStartPoint.y - this.startPoint.y;
      this.para1EndPoint = {x: -1, y: -1};
      this.para2EndPoint = {x: -1, y: -1};
      this.para2StartPoint = {x: -1, y: -1};
      this.para1EndPoint.x = vectorA[0] + vectorB[0] + this.startPoint.x;
      this.para1EndPoint.y = vectorA[1] + vectorB[1] + this.startPoint.y;
      this.para2StartPoint.x = this.startPoint.x - vectorB[0];
      this.para2StartPoint.y = this.startPoint.y - vectorB[1];
      this.para2EndPoint.x = this.endPoint.x - vectorB[0];
      this.para2EndPoint.y = this.endPoint.y - vectorB[1];
      this.getAreaPos();
      this.crossPt0 = getRectCrossPt(this.areaPos, this.startPoint, this.endPoint);
      Plotter.drawLine(context, this.crossPt0[0].x, this.crossPt0[0].y, this.crossPt0[1].x, this.crossPt0[1].y);
      this.crossPt1 = getRectCrossPt(this.areaPos, this.paraStartPoint, this.para1EndPoint);
      Plotter.drawLine(context, this.crossPt1[0].x, this.crossPt1[0].y, this.crossPt1[1].x, this.crossPt1[1].y);
      this.crossPt2 = getRectCrossPt(this.areaPos, this.para2StartPoint, this.para2EndPoint);
      Plotter.drawLine(context, this.crossPt2[0].x, this.crossPt2[0].y, this.crossPt2[1].x, this.crossPt2[1].y);
  };
  var BandLinesPlotter = create_class(CToolPlotter);
  BandLinesPlotter.prototype.__construct = function (name, toolObject) {
      BandLinesPlotter.__super.__construct.call(this, name);
      this.toolObject = toolObject;
      this.ctrlPtsNum = 2;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  BandLinesPlotter.prototype.drawLinesAndInfo = function (context, startPoint, endPoint) {
      context.font = "12px Tahoma";
      context.textAlign = "left";
      context.fillStyle = this.theme.getColor(Theme.Color.LineColorNormal);
      var text;
      if (this.toolObject.state == CToolObject.state.Draw) {
          this.startPtValue = this.toolObject.getPoint(0).getPosIV().v;
          this.endPtValue = this.toolObject.getPoint(1).getPosIV().v;
      }
      this.getAreaPos();
      for (var i = 0; i < this.fiboSequence.length; i++) {
          var stageY = startPoint.y + (100 - this.fiboSequence[i]) / 100 * (endPoint.y - startPoint.y);
          if (stageY > this.areaPos.bottom)
              continue;
          var stageYvalue = this.startPtValue + (100 - this.fiboSequence[i]) / 100 * (this.endPtValue - this.startPtValue);
          Plotter.drawLine(context, this.areaPos.left, stageY, this.areaPos.right, stageY);
          text = this.fiboSequence[i].toFixed(1) + '% ' + stageYvalue.toFixed(1);
          context.fillText(text, this.areaPos.left + 2, stageY - 15);
      }
  };
  BandLinesPlotter.prototype.draw = function (context) {
      this.updateCtrlPtPos();
      this.getAreaPos();
      this.startPoint = this.ctrlPts[1][0];
      this.endPoint = this.ctrlPts[1][1];
      this.drawLinesAndInfo(context, this.startPoint, this.endPoint);
  };
  var DrawFibRetracePlotter = create_class(BandLinesPlotter);
  DrawFibRetracePlotter.prototype.__construct = function (name, toolObject) {
      DrawFibRetracePlotter.__super.__construct.call(this, name, toolObject);
      this.toolObject = toolObject;
      this.fiboSequence = [100.0, 78.6, 61.8, 50.0, 38.2, 23.6, 0.0];
  };
  var DrawBandLinesPlotter = create_class(BandLinesPlotter);
  DrawBandLinesPlotter.prototype.__construct = function (name, toolObject) {
      DrawBandLinesPlotter.__super.__construct.call(this, name, toolObject);
      this.toolObject = toolObject;
      this.fiboSequence = [0, 12.5, 25, 37.5, 50, 62.5, 75, 87.5, 100];
  };
  var DrawFibFansPlotter = create_class(CToolPlotter);
  DrawFibFansPlotter.prototype.__construct = function (name, toolObject) {
      DrawFibFansPlotter.__super.__construct.call(this, name);
      this.toolObject = toolObject;
      this.fiboFansSequence = [0, 38.2, 50, 61.8];
      this.ctrlPtsNum = 2;
      this.ctrlPts = new Array(new Array(this.ctrlPtsNum), new Array(2));
      this.getCtrlPts();
  };
  DrawFibFansPlotter.prototype.drawLinesAndInfo = function (context, startPoint, endPoint) {
      this.drawFibRayLines(context, startPoint, endPoint);
  };
  DrawFibFansPlotter.prototype.draw = function (context) {
      this.updateCtrlPtPos();
      this.getAreaPos();
      this.startPoint = this.ctrlPts[1][0];
      this.endPoint = this.ctrlPts[1][1];
      if (this.startPoint.x == this.endPoint.x && this.startPoint.y == this.endPoint.y) {
          this.endPoint.x += 1;
      }
      this.drawLinesAndInfo(context, this.startPoint, this.endPoint);
  };
  var CDynamicLinePlotter = create_class(NamedObject);
  CDynamicLinePlotter.prototype.__construct = function (name) {
      CDynamicLinePlotter.__super.__construct.call(this, name);
      this.flag = true;
      this.context = ChartManager.getInstance()._overlayContext;
  };
  CDynamicLinePlotter.prototype.getAreaPos = function () {
      var pMgr = ChartManager.getInstance();
      var pArea = pMgr.getArea('frame0.k0.main');
      if (pArea == null) {
          this.areaPos = {
              left: 0,
              top: 0,
              right: 0,
              bottom: 0
          };
          return;
      }
      this.areaPos = {
          left: Math.floor(pArea.getLeft()),
          top: Math.floor(pArea.getTop()),
          right: Math.floor(pArea.getRight()),
          bottom: Math.floor(pArea.getBottom())
      };
  };
  CDynamicLinePlotter.prototype.Draw = function (context) {
      this.getAreaPos();
      var pMgr = ChartManager.getInstance();
      var pTDP = pMgr.getDataSource(this.getDataSourceName());
      if (pTDP == null || !is_instance(pTDP, MainDataSource))
          return;
      this.context.save();
      this.context.rect(this.areaPos.left, this.areaPos.top, this.areaPos.right - this.areaPos.left, this.areaPos.bottom - this.areaPos.top);
      this.context.clip();
      var count = pTDP.getToolObjectCount();
      for (var i = 0; i < count; i++) {
          var toolObject = pTDP.getToolObject(i);
          var state = toolObject.getState();
          switch (state) {
              case CToolObject.state.BeforeDraw:
                  toolObject.getPlotter().theme = ChartManager.getInstance().getTheme(this.getFrameName());
                  toolObject.getPlotter().drawCursor(this.context);
                  break;
              case CToolObject.state.Draw:
                  toolObject.getPlotter().theme = ChartManager.getInstance().getTheme(this.getFrameName());
                  toolObject.getPlotter().updateDraw(this.context);
                  break;
              case CToolObject.state.AfterDraw:
                  toolObject.getPlotter().theme = ChartManager.getInstance().getTheme(this.getFrameName());
                  toolObject.getPlotter().finishDraw(this.context);
                  break;
              default:
                  break;
          }
      }
      var sel = pTDP.getSelectToolObjcet();
      if (sel != null && sel != CToolObject.state.Draw)
          sel.getPlotter().highlight(this.context);
      this.context.restore();
      return;
  };
  
  var isSize = false;
  $('body').on('click', '#sizeIcon', function () {
      isSize = !isSize;
      if (isSize) {
          $(KlineIns.element).css({
              position: 'fixed',
              left: '0',
              right: '0',
              top: '0',
              bottom: '0',
              width: '100%',
              height: '100%',
              zIndex: '10000'
          });
          on_size();
          $('html,body').css({width: '100%', height: '100%', overflow: 'hidden'});
      } else {
          $(KlineIns.element).attr('style', '');
  
          $('html,body').attr('style', '');
  
          on_size(KlineIns.width, KlineIns.height);
          $(KlineIns.element).css({visibility: 'visible', height: KlineIns.height + 'px'});
      }
  });
  
  function KLineMouseEvent() {
      $(document).ready(function () {
          function __resize() {
              if (navigator.userAgent.indexOf('Firefox') >= 0) {
                  setTimeout(function () {
                      on_size(KlineIns.width, KlineIns.height)
                  }, 200);
              } else {
                  on_size(KlineIns.width, KlineIns.height);
              }
          }
  
          $('#chart_overlayCanvas').bind("contextmenu", function (e) {
              e.cancelBubble = true;
              e.returnValue = false;
              e.preventDefault();
              e.stopPropagation();
              return false;
          });
          $(".chart_container .chart_dropdown .chart_dropdown_t")
              .mouseover(function () {
                  var container = $(".chart_container");
                  var title = $(this);
                  var dropdown = title.next();
                  var containerLeft = container.offset().left;
                  var titleLeft = title.offset().left;
                  var containerWidth = container.width();
                  var titleWidth = title.width();
                  var dropdownWidth = dropdown.width();
                  var d = ((dropdownWidth - titleWidth) / 2) << 0;
                  if (titleLeft - d < containerLeft + 4) {
                      d = titleLeft - containerLeft - 4;
                  } else if (titleLeft + titleWidth + d > containerLeft + containerWidth - 4) {
                      d += titleLeft + titleWidth + d - (containerLeft + containerWidth - 4) + 19;
                  } else {
                      d += 4;
                  }
                  dropdown.css({"margin-left": -d});
                  title.addClass("chart_dropdown-hover");
                  dropdown.addClass("chart_dropdown-hover");
              })
              .mouseout(function () {
                  $(this).next().removeClass("chart_dropdown-hover");
                  $(this).removeClass("chart_dropdown-hover");
              });
          $(".chart_dropdown_data")
              .mouseover(function () {
                  $(this).addClass("chart_dropdown-hover");
                  $(this).prev().addClass("chart_dropdown-hover");
              })
              .mouseout(function () {
                  $(this).prev().removeClass("chart_dropdown-hover");
                  $(this).removeClass("chart_dropdown-hover");
              });
          $("#chart_btn_parameter_settings").click(function () {
              $('#chart_parameter_settings').addClass("clicked");
              $(".chart_dropdown_data").removeClass("chart_dropdown-hover");
              $("#chart_parameter_settings").find("th").each(function () {
                  var name = $(this).html();
                  var index = 0;
                  var tmp = ChartSettings.get();
                  var value = tmp.indics[name];
                  $(this.nextElementSibling).find("input").each(function () {
                      if (value != null && index < value.length) {
                          $(this).val(value[index]);
                      }
                      index++;
                  });
              });
          });
          $("#close_settings").click(function () {
              $('#chart_parameter_settings').removeClass("clicked");
          });
          $(".chart_container .chart_toolbar_tabgroup a")
              .click(function () {
                  switch_period($(this).parent().attr('name'));
  
              });
          $("#chart_toolbar_periods_vert ul a").click(function () {
  
              switch_period($(this).parent().attr('name'));
  
          });
  
          $(".market_chooser ul a").click(function () {
              switch_symbol($(this).attr('name'));
          });
  
          $('#chart_show_tools')
              .click(function () {
                  if ($(this).hasClass('selected')) {
                      switch_tools('off');
                  } else {
                      switch_tools('on');
                  }
              });
          $("#chart_toolpanel .chart_toolpanel_button")
              .click(function () {
                  $(".chart_dropdown_data").removeClass("chart_dropdown-hover");
                  $("#chart_toolpanel .chart_toolpanel_button").removeClass("selected");
                  $(this).addClass("selected");
                  var name = $(this).children().attr('name');
                  KlineIns.chartMgr.setRunningMode(ChartManager.DrawingTool[name]);
              });
          $('#chart_show_indicator')
              .click(function () {
                  if ($(this).hasClass('selected')) {
                      switch_indic('off');
                  } else {
                      switch_indic('on');
                  }
              });
          $("#chart_tabbar li a")
              .click(function () {
                  $("#chart_tabbar li a").removeClass('selected');
                  $(this).addClass('selected');
                  var name = $(this).attr('name');
                  var tmp = ChartSettings.get();
                  tmp.charts.indics[1] = name;
                  ChartSettings.save();
                  if (Template.displayVolume == false)
                      ChartManager.getInstance().getChart().setIndicator(1, name);
                  else
                      ChartManager.getInstance().getChart().setIndicator(2, name);
              });
          $("#chart_select_chart_style a")
              .click(function () {
                  $("#chart_select_chart_style a").removeClass('selected');
                  $(this).addClass("selected");
                  var tmp = ChartSettings.get();
                  tmp.charts.chartStyle = $(this)[0].innerHTML;
                  ChartSettings.save();
                  var mgr = ChartManager.getInstance();
                  mgr.setChartStyle("frame0.k0", $(this).html());
                  mgr.redraw();
              });
          $('#chart_dropdown_themes li').click(function () {
              $('#chart_dropdown_themes li a').removeClass('selected');
              var name = $(this).attr('name');
              if (name == 'chart_themes_dark') {
                  switch_theme('dark');
              } else if (name == 'chart_themes_light') {
                  switch_theme('light');
              }
          });
          $("#chart_select_main_indicator a")
              .click(function () {
                  $("#chart_select_main_indicator a").removeClass('selected');
                  $(this).addClass("selected");
                  var name = $(this).attr('name');
                  var tmp = ChartSettings.get();
                  tmp.charts.mIndic = name;
                  ChartSettings.save();
                  var mgr = ChartManager.getInstance();
                  if (!mgr.setMainIndicator("frame0.k0", name))
                      mgr.removeMainIndicator("frame0.k0");
                  mgr.redraw();
              });
          $('#chart_toolbar_theme a').click(function () {
              $('#chart_toolbar_theme a').removeClass('selected');
              if ($(this).attr('name') == 'dark') {
                  switch_theme('dark');
              } else if ($(this).attr('name') == 'light') {
                  switch_theme('light');
              }
          });
          $('#chart_select_theme li a').click(function () {
              $('#chart_select_theme a').removeClass('selected');
              if ($(this).attr('name') == 'dark') {
                  switch_theme('dark');
              } else if ($(this).attr('name') == 'light') {
                  switch_theme('light');
              }
          });
          $('#chart_enable_tools li a').click(function () {
              $('#chart_enable_tools a').removeClass('selected');
              if ($(this).attr('name') == 'on') {
                  switch_tools('on');
              } else if ($(this).attr('name') == 'off') {
                  switch_tools('off');
              }
          });
          $('#chart_enable_indicator li a').click(function () {
              $('#chart_enable_indicator a').removeClass('selected');
              if ($(this).attr('name') == 'on') {
                  switch_indic('on');
              } else if ($(this).attr('name') == 'off') {
                  switch_indic('off');
              }
          });
          $('#chart_language_setting_div li a').click(function () {
  
              $('#chart_language_setting_div a').removeClass('selected');
              if ($(this).attr('name') == 'zh-cn') {
                  chart_switch_language('zh-cn');
              } else if ($(this).attr('name') == 'en-us') {
  
                  chart_switch_language('en-us');
              } else if ($(this).attr('name') == 'zh-tw') {
                  chart_switch_language('zh-tw');
              }
          });
          $(document).keyup(function (e) {
              if (e.keyCode == 46) {
                  ChartManager.getInstance().deleteToolObject();
                  ChartManager.getInstance().redraw('OverlayCanvas', false);
              }
          });
          $("#clearCanvas").click(function () {
              var pDPTool = ChartManager.getInstance().getDataSource("frame0.k0");
              var len = pDPTool.getToolObjectCount();
              for (var i = 0; i < len; i++) {
                  pDPTool.delToolObject();
              }
              ChartManager.getInstance().redraw('OverlayCanvas', false);
          });
          $("#chart_overlayCanvas")
              .mousemove(function (e) {
                  var r = e.target.getBoundingClientRect();
                  var x = e.clientX - r.left;
                  var y = e.clientY - r.top;
                  var mgr = ChartManager.getInstance();
                  if (KlineIns.buttonDown == true) {
                      mgr.onMouseMove("frame0", x, y, true);
                      mgr.redraw("All", false);
                  } else {
                      mgr.onMouseMove("frame0", x, y, false);
                      mgr.redraw("OverlayCanvas");
                  }
              })
              .mouseleave(function (e) {
                  var r = e.target.getBoundingClientRect();
                  var x = e.clientX - r.left;
                  var y = e.clientY - r.top;
                  var mgr = ChartManager.getInstance();
                  mgr.onMouseLeave("frame0", x, y, false);
                  mgr.redraw("OverlayCanvas");
              })
              .mouseup(function (e) {
                  if (e.which != 1) {
                      return;
                  }
                  KlineIns.buttonDown = false;
                  var r = e.target.getBoundingClientRect();
                  var x = e.clientX - r.left;
                  var y = e.clientY - r.top;
                  var mgr = ChartManager.getInstance();
                  mgr.onMouseUp("frame0", x, y);
                  mgr.redraw("All");
              })
              .mousedown(function (e) {
                  if (e.which != 1) {
                      ChartManager.getInstance().deleteToolObject();
                      ChartManager.getInstance().redraw('OverlayCanvas', false);
                      return;
                  }
                  KlineIns.buttonDown = true;
                  var r = e.target.getBoundingClientRect();
                  var x = e.clientX - r.left;
                  var y = e.clientY - r.top;
                  ChartManager.getInstance().onMouseDown("frame0", x, y);
              });
          $("#chart_parameter_settings :input").change(function () {
              var name = $(this).attr("name");
              var index = 0;
              var valueArray = [];
              var mgr = ChartManager.getInstance();
              $("#chart_parameter_settings :input").each(function () {
                  if ($(this).attr("name") == name) {
                      if ($(this).val() != "" && $(this).val() != null && $(this).val() != undefined) {
                          var i = parseInt($(this).val());
                          valueArray.push(i);
                      }
                      index++;
                  }
              });
              if (valueArray.length != 0) {
                  mgr.setIndicatorParameters(name, valueArray);
                  var value = mgr.getIndicatorParameters(name);
                  var cookieArray = [];
                  index = 0;
                  $("#chart_parameter_settings :input").each(function () {
                      if ($(this).attr("name") == name) {
                          if ($(this).val() != "" && $(this).val() != null && $(this).val() != undefined) {
                              $(this).val(value[index].getValue());
                              cookieArray.push(value[index].getValue());
                          }
                          index++;
                      }
                  });
                  var tmp = ChartSettings.get();
                  tmp.indics[name] = cookieArray;
                  ChartSettings.save();
                  mgr.redraw('All', false);
              }
          });
          $("#chart_parameter_settings button").click(function () {
              var name = $(this).parents("tr").children("th").html();
              var index = 0;
              var value = ChartManager.getInstance().getIndicatorParameters(name);
              var valueArray = [];
              $(this).parent().prev().children('input').each(function () {
                  if (value != null && index < value.length) {
                      $(this).val(value[index].getDefaultValue());
                      valueArray.push(value[index].getDefaultValue());
                  }
                  index++;
              });
              ChartManager.getInstance().setIndicatorParameters(name, valueArray);
              var tmp = ChartSettings.get();
              tmp.indics[name] = valueArray;
              ChartSettings.save();
              ChartManager.getInstance().redraw('All', false);
          });
      });
  }
  
  var refresh_counter = 0;
  var refresh_handler = null;
  
  function refresh_function() {
      refresh_counter++;
      var lang = ChartManager.getInstance().getLanguage();
      if (refresh_counter > 3600) {
          var num = new Number(refresh_counter / 3600);
          if (lang == "en-us") {
              $("#chart_updated_time_text").html(num.toFixed(0) + "h");
          } else {
              $("#chart_updated_time_text").html(num.toFixed(0) + "小时");
          }
      } else if (refresh_counter > 60 && refresh_counter <= 3600) {
          var num = new Number(refresh_counter / 60);
          if (lang == "en-us") {
              $("#chart_updated_time_text").html(num.toFixed(0) + "m");
          } else {
              $("#chart_updated_time_text").html(num.toFixed(0) + "分钟");
          }
      } else if (refresh_counter <= 60) {
          if (lang == "en-us") {
              $("#chart_updated_time_text").html(refresh_counter + "s");
          } else {
              $("#chart_updated_time_text").html(refresh_counter + "秒");
          }
      }
  }
  
  function clear_refresh_counter() {
      window.clearInterval(refresh_handler);
      refresh_counter = 0;
      var lang = ChartManager.getInstance().getLanguage();
      if (lang == "en-us") {
          $("#chart_updated_time_text").html(refresh_counter + "s");
      } else {
          $("#chart_updated_time_text").html(refresh_counter + "秒");
      }
      refresh_handler = setInterval(refresh_function, KlineIns.intervalTime);
  }
  
  function RequestData(showLoading) {
      AbortRequest();
      window.clearTimeout(KlineIns.timer);
      if (showLoading == true) {
          $("#chart_loading").addClass("activated");
      }
      if (KlineIns.type == "socket" && KlineIns.socketClient) {
          requestOverSocket();
      } else {
          requestOverHttp();
      }
  }
  
  function parseRequestParam(str) {
      return JSON.parse('{"' + decodeURI(str.replace(/&/g, "\",\"").replace(/=/g, "\":\"")) + '"}')
  }
  
  function requestOverSocket() {
      if (!KlineIns.socketConnected) {
          if (KlineIns.debug) {
              // console.log("DEBUG: socket is not coonnected")
          }
          return;
      }
      if (KlineIns.socketClient && KlineIns.socketClient.ws.readyState == 1) {
          KlineIns.socketClient.send(KlineIns.sendPath, {}, JSON.stringify(parseRequestParam(KlineIns.requestParam)));
          return;
      }
      if (KlineIns.debug) {
          // console.log("DEBUG: socket client is not ready yet ...");
      }
      KlineIns.timer = setTimeout(function () {
          RequestData(true);
      }, 1000);
  }
  function ndata(num){
      var date = new Date(num);
      return Date.parse(date);
  }
  var __that=this;
  function requestOverHttp() {
      if (KlineIns.debug) {
          console.log("DEBUG: " + KlineIns.requestParam);
      }
      $(document).ready(
          KlineIns.G_HTTP_REQUEST = $.ajax({
              type: "POST",
              url: KlineIns.url,
              dataType: 'json',
              data: KlineIns.datas,
              timeout: 30000,
              created: Date.now(),
              beforeSend: function () {
                  this.range = KlineIns.range;
                  this.symbol = KlineIns.symbol;
              },
              success: function (res) {
                  // console.log(res.message.quotation)
                  var datas=res.message.quotation.reverse();
                  var dateses=[];
                  for(let i=0;i<datas.length;i++){
                      var dataList=[];
                      dataList.push((datas[i].e_time*1000),Number(datas[i].start_price),Number(datas[i].highest),Number(datas[i].minimum),Number(datas[i].end_price),Number(datas[i].sum))
                      dateses.push(dataList)
                  }
                  if (KlineIns.G_HTTP_REQUEST) {
                      requestSuccessHandler(dateses);
                  }
              },
              error: function (xhr, textStatus, errorThrown) {
                  if (KlineIns.debug) {
                      console.log(xhr);
                  }
                  if (xhr.status == 200 && xhr.readyState == 4) {
                      return;
                  }
                  KlineIns.timer = setTimeout(function () {
                      RequestData(true);
                  }, KlineIns.intervalTime);
              },
              complete: function () {
                  KlineIns.G_HTTP_REQUEST = null;
              }
          })
      );
  }
  
  function requestSuccessHandler(res) {
      if (KlineIns.debug) {
          // console.log(res);
      }
      // if (!res || !res.success) {
      //     KlineIns.timer = setTimeout(function () {
      //         RequestData(true);
      //     }, KlineIns.intervalTime);
      //     return;
      // }
      var chart = ChartManager.getInstance().getChart();
      chart.setTitle();
      KlineIns.data = eval(res);
  
      var updateDataRes = KlineIns.chartMgr.updateData("frame0.k0", KlineIns.data);
      KlineIns.requestParam = setHttpRequestParam(KlineIns.symbol, KlineIns.range, null, KlineIns.chartMgr.getDataSource("frame0.k0").getLastDate());
  
      var intervalTime = KlineIns.intervalTime < KlineIns.range ? KlineIns.intervalTime : KlineIns.range;
  
      if (!updateDataRes) {
          KlineIns.timer = setTimeout(RequestData, intervalTime);
          return;
      }
      if (KlineIns.data.trades && KlineIns.data.trades.length > 0) {
          KlineTradeIns.pushTrades(KlineIns.data.trades);
          KlineTradeIns.klineTradeInit = true;
      }
      if (KlineIns.data.depths) {
          KlineTradeIns.updateDepth(KlineIns.data.depths);
      }
      clear_refresh_counter();
      KlineIns.timer = setTimeout(TwoSecondThread, intervalTime);
      $("#chart_loading").removeClass("activated");
      ChartManager.getInstance().redraw('All', false);
  }
  
  function AbortRequest() {
      if (KlineIns.type != "socket" || !KlineIns.socketClient) {
          if (KlineIns.G_HTTP_REQUEST && KlineIns.G_HTTP_REQUEST.readyState != 4) {
              KlineIns.G_HTTP_REQUEST.abort();
          }
      }
  }
  
  function TwoSecondThread() {
      var f = KlineIns.chartMgr.getDataSource("frame0.k0").getLastDate();
  
      if (f == -1) {
          KlineIns.requestParam = setHttpRequestParam(KlineIns.symbol, KlineIns.range, KlineIns.limit, null);
      } else {
          KlineIns.requestParam = setHttpRequestParam(KlineIns.symbol, KlineIns.range, null, f.toString());
      }
  
      RequestData();
  }
  
  function readCookie() {
      ChartSettings.get();
      ChartSettings.save();
      var tmp = ChartSettings.get();
      ChartManager.getInstance().setChartStyle('frame0.k0', tmp.charts.chartStyle);
      var symbol = tmp.charts.symbol;
      if (!KlineIns.init) {
          symbol = KlineIns.symbol;
          KlineIns.init = true;
      }
      KlineIns.symbol = symbol;
      switch_symbol_selected(symbol);
      var period = tmp.charts.period;
      switch_period(period);
      $('#chart_period_' + period + '_v a').addClass('selected');
      $('#chart_period_' + period + '_h a').addClass('selected');
      if (tmp.charts.indicsStatus == 'close') {
          switch_indic('off');
      } else if (tmp.charts.indicsStatus == 'open') {
          switch_indic('on');
      }
      var main_indic = $('#chart_select_main_indicator');
      main_indic.find('a').each(function () {
          if ($(this).attr('name') == tmp.charts.mIndic) {
              $(this).addClass('selected');
          }
      });
      var chart_style = $('#chart_select_chart_style');
      chart_style.find('a').each(function () {
          if ($(this)[0].innerHTML == tmp.charts.chartStyle) {
              $(this).addClass('selected');
          }
      });
      ChartManager.getInstance().getChart().setMainIndicator(tmp.charts.mIndic);
      ChartManager.getInstance().setThemeName('frame0', tmp.theme);
      switch_tools('off');
      if (tmp.theme == 'Dark') {
          switch_theme('dark');
      } else if (tmp.theme == 'Light') {
          switch_theme('light');
      }
      chart_switch_language(tmp.language || "zh-cn");
  }
  
  
  function setHttpRequestParam(mark_from, range, limit, since) {
      var str = "symbol=" + mark_from + "&range=" + range;
      if (limit != null)
          str += "&limit=" + limit;
      else
          str += "&since=" + since;
      if (KlineTradeIns.tradeDate.getTime() != 0) {
          str += "&prevTradeTime=" + KlineTradeIns.tradeDate.getTime();
      }
      return str;
  }
  
  function refreshTemplate() {
      KlineIns.chartMgr = DefaultTemplate.loadTemplate("frame0.k0", "");
      ChartManager.getInstance().redraw('All', true);
  }
  
  function getRectCrossPt(rect, startPt, endPt) {
      var crossPt;
      var firstPt = {x: -1, y: -1};
      var secondPt = {x: -1, y: -1};
      var xdiff = endPt.x - startPt.x;
      var ydiff = endPt.y - startPt.y;
      if (Math.abs(xdiff) < 2) {
          firstPt = {x: startPt.x, y: rect.top};
          secondPt = {x: endPt.x, y: rect.bottom};
          crossPt = [firstPt, secondPt];
          return crossPt;
      }
      var k = ydiff / xdiff;
      secondPt.x = rect.right;
      secondPt.y = startPt.y + (rect.right - startPt.x) * k;
      firstPt.x = rect.left;
      firstPt.y = startPt.y + (rect.left - startPt.x) * k;
      crossPt = [firstPt, secondPt];
      return crossPt;
  }
  
  function chart_switch_language(lang) {
      var tmp = lang.replace(/-/, '_');
      $('#chart_language_switch_tmp').find('span').each(function () {
          var name = $(this).attr('name');
          var attr = $(this).attr(tmp);
          name = '.' + name;
          var obj = $(name)[0];
  
          if (!obj)
              return;
          $(name).each(function () {
  
              $(this)[0].innerHTML = attr;
          });
      });
      $("#chart_language_setting_div li a[name='" + lang + "']").addClass("selected");
      ChartManager.getInstance().setLanguage(lang);
      ChartManager.getInstance().getChart().setTitle();
      var tmp = ChartSettings.get();
      tmp.language = lang;
      ChartSettings.save();
      KlineIns.onLangChange(lang);
  }
  
  function on_size(w, h) {
      var width = w || window.innerWidth;
      var chartWidth = KlineIns.showTrade ? (width - KlineIns.tradeWidth) : width;
      var height = h || window.innerHeight;
      var container = $(KlineIns.element);
      container.css({
          width: width + 'px',
          height: height + 'px'
      });
      var toolBar = $('#chart_toolbar');
      var toolPanel = $('#chart_toolpanel');
      var canvasGroup = $('#chart_canvasGroup');
      var tabBar = $('#chart_tabbar');
      var toolPanelShown = toolPanel[0].style.display != 'inline' ? false : true;
      var tabBarShown = tabBar[0].style.display != 'block' ? false : true;
      var toolBarRect = {};
      toolBarRect.x = 0;
      toolBarRect.y = 0;
      toolBarRect.w = chartWidth;
      toolBarRect.h = 29;
      var toolPanelRect = {};
      toolPanelRect.x = 0;
      toolPanelRect.y = toolBarRect.h + 1;
      toolPanelRect.w = toolPanelShown ? 32 : 0;
      toolPanelRect.h = height - toolPanelRect.y;
      var tabBarRect = {};
      tabBarRect.w = toolPanelShown ? chartWidth - (toolPanelRect.w + 1 ) : chartWidth;
      tabBarRect.h = tabBarShown ? 22 : -1;
      tabBarRect.x = chartWidth - tabBarRect.w;
      tabBarRect.y = height - (tabBarRect.h + 1 );
      var canvasGroupRect = {};
      canvasGroupRect.x = tabBarRect.x;
      canvasGroupRect.y = toolPanelRect.y;
      canvasGroupRect.w = tabBarRect.w;
      canvasGroupRect.h = tabBarRect.y - toolPanelRect.y;
      toolBar.css({
          left: toolBarRect.x + 'px',
          top: toolBarRect.y + 'px',
          width: toolBarRect.w + 'px',
          height: toolBarRect.h + 'px'
      });
      if (toolPanelShown) {
          toolPanel.css({
              left: toolPanelRect.x + 'px',
              top: toolPanelRect.y + 'px',
              width: toolPanelRect.w + 'px',
              height: toolPanelRect.h + 'px'
          });
      }
  
      canvasGroup.css({
          left: canvasGroupRect.x + 'px',
          top: canvasGroupRect.y + 'px',
          width: canvasGroupRect.w + 'px',
          height: canvasGroupRect.h + 'px'
      });
      var mainCanvas = $('#chart_mainCanvas')[0];
      var overlayCanvas = $('#chart_overlayCanvas')[0];
      mainCanvas.width = canvasGroupRect.w;
      mainCanvas.height = canvasGroupRect.h;
      overlayCanvas.width = canvasGroupRect.w;
      overlayCanvas.height = canvasGroupRect.h;
      if (tabBarShown) {
          tabBar.css({
              left: tabBarRect.x + 'px',
              top: tabBarRect.y + 'px',
              width: tabBarRect.w + 'px',
              height: tabBarRect.h + 'px'
          });
      }
      var dlgSettings = $("#chart_parameter_settings");
      dlgSettings.css({
          left: (chartWidth - dlgSettings.width()) >> 1,
          top: (height - dlgSettings.height()) >> 1
      });
      var dlgLoading = $("#chart_loading");
      dlgLoading.css({
          left: (chartWidth - dlgLoading.width()) >> 1,
          top: (height - dlgLoading.height()) >> 2
      });
      var domElemCache = $('#chart_dom_elem_cache');
      var rowTheme = $('#chart_select_theme')[0];
      var rowTools = $('#chart_enable_tools')[0];
      var rowIndic = $('#chart_enable_indicator')[0];
      var periodsVert = $('#chart_toolbar_periods_vert');
      var periodsHorz = $('#chart_toolbar_periods_horz')[0];
      var showIndic = $('#chart_show_indicator')[0];
      var showTools = $('#chart_show_tools')[0];
      var selectTheme = $('#chart_toolbar_theme')[0];
      var dropDownSettings = $('#chart_dropdown_settings');
      var periodsVertNW = periodsVert[0].offsetWidth;
      var periodsHorzNW = periodsVertNW + periodsHorz.offsetWidth;
      var showIndicNW = periodsHorzNW + showIndic.offsetWidth + 4;
      var showToolsNW = showIndicNW + showTools.offsetWidth + 4;
      var selectThemeNW = showToolsNW + selectTheme.offsetWidth;
      var dropDownSettingsW = dropDownSettings.find(".chart_dropdown_t")[0].offsetWidth + 150;
      periodsVertNW += dropDownSettingsW;
      periodsHorzNW += dropDownSettingsW;
      showIndicNW += dropDownSettingsW;
      showToolsNW += dropDownSettingsW;
      selectThemeNW += dropDownSettingsW;
      if (chartWidth < periodsHorzNW) {
          domElemCache.append(periodsHorz);
      } else {
          periodsVert.after(periodsHorz);
      }
      if (chartWidth < showIndicNW) {
          domElemCache.append(showIndic);
          rowIndic.style.display = "";
      } else {
          dropDownSettings.before(showIndic);
          rowIndic.style.display = "none";
      }
      if (chartWidth < showToolsNW) {
          domElemCache.append(showTools);
          rowTools.style.display = "";
      } else {
          dropDownSettings.before(showTools);
          rowTools.style.display = "none";
      }
      if (chartWidth < selectThemeNW) {
          domElemCache.append(selectTheme);
          rowTheme.style.display = "";
      } else {
          dropDownSettings.before(selectTheme);
          rowTheme.style.display = "none";
      }
  
      ChartManager.getInstance().redraw('All', true);
  
      KlineIns.onResize(width, height);
  }
  
  function mouseWheel(e, delta) {
      ChartManager.getInstance().scale(delta > 0 ? 1 : -1);
      ChartManager.getInstance().redraw("All", true);
      return false;
  }
  
  function switch_theme(name) {
  
      $('#chart_toolbar_theme a').removeClass('selected');
      $('#chart_select_theme a').removeClass('selected');
      $('#chart_toolbar_theme').find('a').each(function () {
          if ($(this).attr('name') == name) {
              $(this).addClass('selected');
          }
      });
      $('#chart_select_theme a').each(function () {
          if ($(this).attr('name') == name) {
              $(this).addClass('selected');
          }
      });
      $(".chart_container").attr('class', "chart_container " + name);
      $(".marketName_ a").attr('class', name);
  
      if (name == 'dark') {
          $(".trade_container").addClass("dark").removeClass("light");
          ChartManager.getInstance().setThemeName('frame0', 'Dark');
          var tmp = ChartSettings.get();
          tmp.theme = 'Dark';
          ChartSettings.save();
      } else if (name == 'light') {
          $(".trade_container").addClass("light").removeClass("dark");
          ChartManager.getInstance().setThemeName('frame0', 'Light');
          var tmp = ChartSettings.get();
          tmp.theme = 'Light';
          ChartSettings.save();
      }
      var a = {};
      a.command = "set current theme";
      a.content = name;
      $('#chart_output_interface_text').val(JSON.stringify(a));
      $('#chart_output_interface_submit').submit();
      window._current_theme_change.raise(name);
      ChartManager.getInstance().redraw();
  
      KlineIns.onThemeChange(name);
  }
  
  function switch_tools(name) {
      $(".chart_dropdown_data").removeClass("chart_dropdown-hover");
      $("#chart_toolpanel .chart_toolpanel_button").removeClass("selected");
      $('#chart_enable_tools a').removeClass('selected');
      if (name == 'on') {
          $('#chart_show_tools').addClass('selected');
          $('#chart_enable_tools a').each(function () {
              if ($(this).attr('name') == 'on') {
                  $(this).addClass('selected');
              }
          });
          $('#chart_toolpanel')[0].style.display = 'inline';
          if (ChartManager.getInstance()._drawingTool == ChartManager.DrawingTool.Cursor) {
              $('#chart_Cursor').parent().addClass('selected');
          } else if (ChartManager.getInstance()._drawingTool == ChartManager.DrawingTool.CrossCursor) {
              $('#chart_CrossCursor').parent().addClass('selected');
          }
      } else if (name == 'off') {
          $('#chart_show_tools').removeClass('selected');
          $('#chart_enable_tools a').each(function () {
              if ($(this).attr('name') == 'off') {
                  $(this).addClass('selected');
              }
          });
          $('#chart_toolpanel')[0].style.display = 'none';
          ChartManager.getInstance().setRunningMode(ChartManager.getInstance()._beforeDrawingTool);
          ChartManager.getInstance().redraw("All", true);
      }
      on_size(KlineIns.width, KlineIns.height);
  }
  
  function switch_indic(name) {
      $('#chart_enable_indicator a').removeClass('selected');
      $("#chart_enable_indicator a[name='" + name + "']").addClass('selected');
      if (name == 'on') {
          $('#chart_show_indicator').addClass('selected');
          var tmp = ChartSettings.get();
          tmp.charts.indicsStatus = 'open';
          ChartSettings.save();
          var value = tmp.charts.indics[1];
          if (Template.displayVolume == false)
              ChartManager.getInstance().getChart().setIndicator(2, value);
          else
              ChartManager.getInstance().getChart().setIndicator(2, value);
          $("#chart_tabbar").find('a').each(function () {
              if ($(this).attr('name') == value)
                  $(this).addClass('selected');
          });
          $('#chart_tabbar')[0].style.display = 'block';
      } else if (name == 'off') {
          $('#chart_show_indicator').removeClass('selected');
          ChartManager.getInstance().getChart().setIndicator(2, 'NONE');
          var tmp = ChartSettings.get();
          tmp.charts.indicsStatus = 'close';
          ChartSettings.save();
          $('#chart_tabbar')[0].style.display = 'none';
          $("#chart_tabbar a").removeClass("selected");
      }
      on_size(KlineIns.width, KlineIns.height);
  }
  
  function switch_period(name) {
  
      $(".chart_container .chart_toolbar_tabgroup a").removeClass("selected");
      $("#chart_toolbar_periods_vert ul a").removeClass("selected");
      $(".chart_container .chart_toolbar_tabgroup a").each(function () {
          if ($(this).parent().attr('name') == name) {
              $(this).addClass('selected');
          }
      });
      $("#chart_toolbar_periods_vert ul a").each(function () {
          if ($(this).parent().attr('name') == name) {
              $(this).addClass('selected');
          }
      });
      ChartManager.getInstance().showCursor();
      calcPeriodWeight(name);
      if (name == 'line') {
          ChartManager.getInstance().getChart().strIsLine = true;
          ChartManager.getInstance().setChartStyle('frame0.k0', 'Line');
          ChartManager.getInstance().getChart().setCurrentPeriod('line');
          var settings = ChartSettings.get();
          settings.charts.period = name;
          ChartSettings.save();
          return;
      }
      ChartManager.getInstance().getChart().strIsLine = false;
      var p = KlineIns.tagMapPeriod[name];
      ChartManager.getInstance().setChartStyle('frame0.k0', ChartSettings.get().charts.chartStyle);
      ChartManager.getInstance().getChart().setCurrentPeriod(p);
      var settings = ChartSettings.get();
      settings.charts.period = name;
      ChartSettings.save();
  }
  
  function reset(symbol) {
      KlineIns.symbol = symbol;
  
      if (KlineIns.showTrade) {
          KlineTradeIns.reset(symbol);
      }
  }
  
  function switch_symbol_selected(symbol) {
      reset(symbol);
      $(".market_chooser ul a").removeClass("selected");
      $(".market_chooser ul a[name='" + symbol + "']").addClass("selected");
      ChartManager.getInstance().getChart()._symbol = symbol;
      var settings = ChartSettings.get();
      settings.charts.symbol = symbol;
      ChartSettings.save();
  }
  
  function switch_symbol(symbol) {
      switch_symbol_selected(symbol);
      var settings = ChartSettings.get();
      if (settings.charts.period == "line") {
          ChartManager.getInstance().getChart().strIsLine = true;
          ChartManager.getInstance().setChartStyle('frame0.k0', 'Line');
      } else {
          ChartManager.getInstance().getChart().strIsLine = false;
          ChartManager.getInstance().setChartStyle('frame0.k0', ChartSettings.get().charts.chartStyle);
      }
      ChartManager.getInstance().getChart().setSymbol(symbol);
  }
  
  function calcPeriodWeight(period) {
      var index = period;
      if (period != 'line')
          index = KlineIns.periodMap[KlineIns.tagMapPeriod[period]];
      var period_weight = ChartSettings.get().charts.period_weight;
      for (var i in period_weight) {
          if (period_weight[i] > period_weight[index]) {
              period_weight[i] -= 1;
          }
      }
      period_weight[index] = 8;
      ChartSettings.save();
  }
  
  function socketConnect() {
      KlineIns.socketConnected = true;
      if (KlineIns.socketClient && KlineIns.socketClient.ws.readyState == 1) {
          console.log('DEBUG: already connected');
          return;
      }
      if ( KlineIns.enableSockjs ) {
          var socket = new SockJS(KlineIns.url);
          KlineIns.socketClient = Stomp.over(socket);
      } else {
          KlineIns.socketClient = Stomp.client(KlineIns.url);
      }
      if (!KlineIns.debug) {
          KlineIns.socketClient.debug = null;
      }
      KlineIns.socketClient.connect({}, function () {
          KlineIns.socketClient.subscribe(KlineIns.subscribePath, function (res) {
              requestSuccessHandler(JSON.parse(res.body));
          });
      }, function () {
          KlineIns.socketClient.disconnect();
          console.log("DEBUG: reconnect in 5 seconds ...");
          setTimeout(function () {
              socketConnect();
          }, 5000);
      });
  }
  
  var template_str = "\n" +
      "<div class=\"trade_container dark\">\n" +
      "        <div class=\"m_cent\">\n" +
      "            <div class=\"m_guadan\">\n" +
      "                <div class=\"symbol-title\">\n" +
      "                    <a class=\"dark\">test</a>\n" +
      "                    \n" +
      "                </div>\n" +
      "                <div id=\"orderbook\">\n" +
      "                    <div id=\"asks\">\n" +
      "                        <div class=\"table\"> </div>\n" +
      "                   </div>\n" +
      "                   <div id=\"gasks\">\n" +
      "                        <div class=\"table\"> </div>\n" +
      "                   </div>\n" +
      "                   <div id=\"price\" class=\"green\"></div>\n" +
      "                   <div id=\"bids\">\n" +
      "                        <div class=\"table\"> </div>\n" +
      "                    </div>\n" +
      "                    <div id=\"gbids\">\n" +
      "                        <div class=\"table\"> </div>\n" +
      "                    </div>\n" +
      "                </div>\n" +
      "                <div id=\"trades\" class=\"trades\">\n" +
      "                <div class=\"trades_list\"> </div>\n" +
      "                </div>\n" +
      "            \n" +
      "            </div>\n" +
      "        \n" +
      "        </div>\n" +
      "</div>\n" +
  
      "<div class=\"chart_container dark\">\n" +
      "            <div id=\"chart_dom_elem_cache\"></div>\n" +
      "            <!-- ToolBar -->\n" +
      "            <div id=\"chart_toolbar\">\n" +
      "                <div class=\"chart_toolbar_minisep\"></div>\n" +
      "                <!-- Periods -->\n" +
      "                <div class=\"chart_dropdown\" id=\"chart_toolbar_periods_vert\">\n" +
      "                    <div class=\"chart_dropdown_t\"><a class=\"chart_str_period\">周期</a></div>\n" +
      "                    <div class=\"chart_dropdown_data\" style=\"margin-left: -58px;\">\n" +
      "                        <table>\n" +
      "                            <tbody>\n" +
      "                            <tr>\n" +
      "                                <td>\n" +
      "                                    <ul>\n" +
      "                                        <li id=\"chart_period_1w_v\" style=\"display:none;\" name=\"1w\"><a class=\"chart_str_period_1w\">周线</a></li>\n" +
      "                                        <li id=\"chart_period_3d_v\" style=\"display:none;\" name=\"3d\"><a class=\"chart_str_period_3d\">3日</a></li>\n" +
      "                                        <li id=\"chart_period_1d_v\" style=\"display:none;\" name=\"1d\"><a class=\"chart_str_period_1d\">日线</a></li>\n" +
      "                                        <li id=\"chart_period_12h_v\" style=\"display:none;\" name=\"12h\"><a class=\"chart_str_period_12h\">12小时</a></li>\n" +
      "                                        <li id=\"chart_period_6h_v\" style=\"display:none;\" name=\"6h\"><a class=\"chart_str_period_6h\">6小时</a></li>\n" +
      "                                        <li id=\"chart_period_4h_v\" style=\"display:none;\" name=\"4h\"><a class=\"chart_str_period_4h\">4小时</a></li>\n" +
      "                                        <li id=\"chart_period_2h_v\" style=\"display:none;\" name=\"2h\"><a class=\"chart_str_period_2h\">2小时</a></li>\n" +
      "                                        <li id=\"chart_period_1h_v\" style=\"display:none;\" name=\"1h\"><a class=\"chart_str_period_1h\">1小时</a></li>\n" +
      "                                    </ul>\n" +
      "                                </td>\n" +
      "                            </tr>\n" +
      "                            <tr>\n" +
      "                                <td>\n" +
      "                                    <ul>\n" +
      "                                        <li id=\"chart_period_30m_v\" style=\"display:none;\" name=\"30m\"><a class=\"chart_str_period_30m\">30分钟</a></li>\n" +
      "                                        <li id=\"chart_period_15m_v\" style=\"display:none;\" name=\"15m\"><a class=\"chart_str_period_15m\">15分钟</a></li>\n" +
      "                                        <li id=\"chart_period_5m_v\" style=\"display:none;\" name=\"5m\"><a class=\"chart_str_period_5m\">5分钟</a></li>\n" +
      "                                        <li id=\"chart_period_3m_v\" style=\"display:none;\" name=\"3m\"><a class=\"chart_str_period_3m\">3分钟</a></li>\n" +
      "                                        <li id=\"chart_period_1m_v\" style=\"display:none;\" name=\"1m\"><a class=\"chart_str_period_1m selected\">1分钟</a></li>\n" +
      "                                        <li id=\"chart_period_line_v\" style=\"display:none;\" name=\"line\"><a class=\"chart_str_period_line\">分时</a>\n" +
      "                                        </li>\n" +
      "                                    </ul>\n" +
      "                                </td>\n" +
      "                            </tr>\n" +
      "                            </tbody>\n" +
      "                        </table>\n" +
      "                    </div>\n" +
      "                </div>\n" +
      "                <div id=\"chart_toolbar_periods_horz\">\n" +
      "                    <ul class=\"chart_toolbar_tabgroup\" style=\"padding-left:5px; padding-right:11px;\">\n" +
      "                        <li id=\"chart_period_1w_h\" name=\"1w\" style=\"display: none;\"><a\ class=\"chart_str_period_1w\">周线</a></li>\n" +
      "                        <li id=\"chart_period_3d_h\" name=\"3d\" style=\"display: none;\"><a\ class=\"chart_str_period_3d\">3日</a></li>\n" +
      "                        <li id=\"chart_period_1d_h\" name=\"1d\" style=\"display: none;\"><a class=\"chart_str_period_1d\">日线</a></li>\n" +
      "                        <li id=\"chart_period_12h_h\" name=\"12h\" style=\"display: none;\"><a class=\"chart_str_period_12h\">12小时</a></li>\n" +
      "                        <li id=\"chart_period_6h_h\" name=\"6h\" style=\"display: none;\"><a class=\"chart_str_period_6h\">6小时</a></li>\n" +
      "                        <li id=\"chart_period_4h_h\" name=\"4h\" style=\"display: none;\"><a class=\"chart_str_period_4h\">4小时</a></li>\n" +
      "                        <li id=\"chart_period_2h_h\" name=\"2h\" style=\"display: none;\"><a class=\"chart_str_period_2h\">2小时</a></li>\n" +
      "                        <li id=\"chart_period_1h_h\" name=\"1h\" style=\"display: none;\"><a class=\"chart_str_period_1h\">1小时</a></li>\n" +
      "                        <li id=\"chart_period_30m_h\" name=\"30m\" style=\"display: none;\"><a class=\"chart_str_period_30m\">30分钟</a></li>\n" +
      "                        <li id=\"chart_period_15m_h\" name=\"15m\" style=\"display: none;\"><a class=\"chart_str_period_15m\">15分钟</a></li>\n" +
      "                        <li id=\"chart_period_5m_h\" name=\"5m\" style=\"display: none;\"><a class=\"chart_str_period_5m\">5分钟</a></li>\n" +
      "                        <li id=\"chart_period_3m_h\" name=\"3m\" style=\"display: none;\"><a class=\"chart_str_period_3m\">3分钟</a></li>\n" +
      "                        <li id=\"chart_period_1m_h\" name=\"1m\" style=\"display: none;\"><a class=\"chart_str_period_1m selected\">1分钟</a></li>\n" +
      "                        <li id=\"chart_period_line_h\" name=\"line\" style=\"display: none;\"><a class=\"chart_str_period_line\">分时</a></li>\n" +
      "                    </ul>\n" +
      "                </div>\n" +
      "                <div id=\"chart_show_indicator\" class=\"chart_toolbar_button chart_str_indicator_cap selected\">技术指标</div>\n" +
      "                <div id=\"chart_show_tools\" class=\"chart_toolbar_button chart_str_tools_cap\">画线工具</div>\n" +
      "                <div id=\"chart_toolbar_theme\">\n" +
      "                    <div class=\"chart_toolbar_label chart_str_theme_cap\">主题选择</div>\n" +
      "                    <a name=\"dark\" class=\"chart_icon chart_icon_theme_dark selected\"></a>\n" +
      "                    <a name=\"light\" class=\"chart_icon chart_icon_theme_light\"></a>\n" +
      "                </div>\n" +
      "                <div class=\"chart_dropdown\" id=\"chart_dropdown_settings\">\n" +
      "                    <div class=\"chart_dropdown_t\"><a class=\"chart_str_settings\">更多</a></div>\n" +
      "                    <div class=\"chart_dropdown_data\" style=\"margin-left: -142px;\">\n" +
      "                        <table>\n" +
      "                            <tbody>\n" +
      "                            <tr id=\"chart_select_main_indicator\">\n" +
      "                                <td class=\"chart_str_main_indicator\">主指标</td>\n" +
      "                                <td>\n" +
      "                                    <ul>\n" +
      "                                        <li><a name=\"MA\" class=\"selected\">MA</a></li>\n" +
      "                                        <li><a name=\"EMA\" class=\"\">EMA</a></li>\n" +
      "                                        <li><a name=\"BOLL\" class=\"\">BOLL</a></li>\n" +
      "                                        <li><a name=\"SAR\" class=\"\">SAR</a></li>\n" +
      "                                        <li><a name=\"NONE\" class=\"\">None</a></li>\n" +
      "                                    </ul>\n" +
      "                                </td>\n" +
      "                            </tr>\n" +
      "                            <tr id=\"chart_select_chart_style\">\n" +
      "                                <td class=\"chart_str_chart_style\">主图样式</td>\n" +
      "                                <td>\n" +
      "                                    <ul>\n" +
      "                                        <li><a class=\"selected\">CandleStick</a></li>\n" +
      "                                        <li><a>CandleStickHLC</a></li>\n" +
      "                                        <li><a class=\"\">OHLC</a></li>\n" +
      "                                    </ul>\n" +
      "                                </td>\n" +
      "                            </tr>\n" +
      "                            <tr id=\"chart_select_theme\" style=\"display: none;\">\n" +
      "                                <td class=\"chart_str_theme\">主题选择</td>\n" +
      "                                <td>\n" +
      "                                    <ul>\n" +
      "                                        <li>\n" +
      "                                            <a name=\"dark\" class=\"chart_icon chart_icon_theme_dark selected\"></a>\n" +
      "                                        </li>\n" +
      "                                        <li>\n" +
      "                                            <a name=\"light\" class=\"chart_icon chart_icon_theme_light\"></a>\n" +
      "                                        </li>\n" +
      "                                    </ul>\n" +
      "                                </td>\n" +
      "                            </tr>\n" +
      "                            <tr id=\"chart_enable_tools\" style=\"display: none;\">\n" +
      "                                <td class=\"chart_str_tools\">画线工具</td>\n" +
      "                                <td>\n" +
      "                                    <ul>\n" +
      "                                        <li><a name=\"on\" class=\"chart_str_on\">开启</a></li>\n" +
      "                                        <li><a name=\"off\" class=\"chart_str_off selected\">关闭</a></li>\n" +
      "                                    </ul>\n" +
      "                                </td>\n" +
      "                            </tr>\n" +
      "                            <tr id=\"chart_enable_indicator\" style=\"display: none;\">\n" +
      "                                <td class=\"chart_str_indicator\">技术指标</td>\n" +
      "                                <td>\n" +
      "                                    <ul>\n" +
      "                                        <li><a name=\"on\" class=\"chart_str_on selected\">开启</a></li>\n" +
      "                                        <li><a name=\"off\" class=\"chart_str_off\">关闭</a></li>\n" +
      "                                    </ul>\n" +
      "                                </td>\n" +
      "                            </tr>\n" +
      "                            <tr>\n" +
      "                                <td></td>\n" +
      "                                <td>\n" +
      "                                    <ul>\n" +
      "                                        <li><a id=\"chart_btn_parameter_settings\"\n" +
      "                                               class=\"chart_str_indicator_parameters\">指标参数设置</a>\n" +
      "                                        </li>\n" +
      "                                    </ul>\n" +
      "                                </td>\n" +
      "                            </tr>\n" +
      "                            </tbody>\n" +
      "                        </table>\n" +
      "                    </div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_dropdown\" id=\"chart_language_setting_div\" style=\"padding-left: 5px;\">\n" +
      "                    <div class=\"chart_dropdown_t\">\n" +
      "                        <a class=\"chart_language_setting\">语言(LANG)</a>\n" +
      "                    </div>\n" +
      "                    <div class=\"chart_dropdown_data\" style=\"padding-top: 15px; margin-left: -12px;\">\n" +
      "                        <ul>\n" +
      "                            <li style=\"height: 25px;\"><a name=\"zh-cn\" class=\"selected\">简体中文(zh-CN)</a></li>\n" +
      "                            <li style=\"height: 25px;\"><a name=\"en-us\">English(en-US)</a></li>\n" +
      "                            <li style=\"height: 25px;\"><a name=\"zh-tw\">繁體中文(zh-HK)</a></li>\n" +
      "                        </ul>\n" +
      "                    </div>\n" +
      "                </div>\n" +
      "                <div id=\"chart_updated_time\">\n" +
      "                    <div id=\"sizeIcon\" class=\"chart_BoxSize\"></div>\n" +
      "                </div>\n" +
      "            </div>\n" +
      "            <!-- ToolPanel -->\n" +
      "            <div id=\"chart_toolpanel\">\n" +
      "                <div class=\"chart_toolpanel_separator\"></div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_Cursor\" name=\"Cursor\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_cursor\">光标</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_CrossCursor\" name=\"CrossCursor\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_cross_cursor\">十字光标</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_SegLine\" name=\"SegLine\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_seg_line\">线段</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_StraightLine\" name=\"StraightLine\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_straight_line\">直线</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_RayLine\" name=\"RayLine\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_ray_line\">射线</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_ArrowLine\" name=\"ArrowLine\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_arrow_line\">箭头</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_HoriSegLine\" name=\"HoriSegLine\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_horz_seg_line\">水平线段</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_HoriStraightLine\" name=\"HoriStraightLine\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_horz_straight_line\">水平直线</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_HoriRayLine\" name=\"HoriRayLine\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_horz_ray_line\">水平射线</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_VertiStraightLine\" name=\"VertiStraightLine\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_vert_straight_line\">垂直直线</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_PriceLine\" name=\"PriceLine\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_price_line\">价格线</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_TriParallelLine\" name=\"TriParallelLine\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_tri_parallel_line\">价格通道线</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_BiParallelLine\" name=\"BiParallelLine\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_bi_parallel_line\">平行直线</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_BiParallelRayLine\" name=\"BiParallelRayLine\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_bi_parallel_ray\">平行射线</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_DrawFibRetrace\" name=\"DrawFibRetrace\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_fib_retrace\">斐波纳契回调</div>\n" +
      "                </div>\n" +
      "                <div class=\"chart_toolpanel_button\">\n" +
      "                    <div class=\"chart_toolpanel_icon\" id=\"chart_DrawFibFans\" name=\"DrawFibFans\"></div>\n" +
      "                    <div class=\"chart_toolpanel_tip chart_str_fib_fans\">斐波纳契扇形</div>\n" +
      "                </div>\n" +
      "                <div style=\"padding-left: 3px;padding-top: 10px;\">\n" +
      "                    <button style=\"color: red;\" id=\"clearCanvas\" title=\"Clear All\">X</button>\n" +
      "                </div>\n" +
      "            </div>\n" +
      "            <!-- Canvas Group -->\n" +
      "            <div id=\"chart_canvasGroup\" class=\"temp\">\n" +
      "                <canvas class=\"chart_canvas\" id=\"chart_mainCanvas\"\n" +
      "                        style=\"cursor: default;\"></canvas>\n" +
      "                <canvas class=\"chart_canvas\" id=\"chart_overlayCanvas\"\n" +
      "                        style=\"cursor: default;\"></canvas>\n" +
      "            </div>\n" +
      "            <!-- TabBar -->\n" +
      "            <div id=\"chart_tabbar\"\">\n" +
      "                <ul>\n" +
      "                    <li><a name=\"MACD\" class=\"\">MACD</a></li>\n" +
      "                    <li><a name=\"KDJ\" class=\"\">KDJ</a></li>\n" +
      "                    <li><a name=\"StochRSI\" class=\"\">StochRSI</a></li>\n" +
      "                    <li><a name=\"RSI\" class=\"\">RSI</a></li>\n" +
      "                    <li><a name=\"DMI\" class=\"\">DMI</a></li>\n" +
      "                    <li><a name=\"OBV\" class=\"\">OBV</a></li>\n" +
      "                    <li><a name=\"BOLL\" class=\"\">BOLL</a></li>\n" +
      "                    <li><a name=\"SAR\" class=\"\">SAR</a></li>\n" +
      "                    <li><a name=\"DMA\" class=\"\">DMA</a></li>\n" +
      "                    <li><a name=\"TRIX\" class=\"\">TRIX</a></li>\n" +
      "                    <li><a name=\"BRAR\" class=\"\">BRAR</a></li>\n" +
      "                    <li><a name=\"VR\" class=\"\">VR</a></li>\n" +
      "                    <li><a name=\"EMV\" class=\"\">EMV</a></li>\n" +
      "                    <li><a name=\"WR\" class=\"\">WR</a></li>\n" +
      "                    <li><a name=\"ROC\" class=\"\">ROC</a></li>\n" +
      "                    <li><a name=\"MTM\" class=\"\">MTM</a></li>\n" +
      "                    <li><a name=\"PSY\">PSY</a></li>\n" +
      "                </ul>\n" +
      "            </div>\n" +
      "            <!-- Parameter Settings -->\n" +
      "            <div id=\"chart_parameter_settings\">\n" +
      "                <h2 class=\"chart_str_indicator_parameters\">指标参数设置</h2>\n" +
      "                <table>\n" +
      "                    <tbody>\n" +
      "                    <tr>\n" +
      "                        <th>MA</th>\n" +
      "                        <td><input name=\"MA\"><input name=\"MA\"><input name=\"MA\"><input name=\"MA\"><br><input\n" +
      "                                name=\"MA\"><input\n" +
      "                                name=\"MA\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                        <th>DMA</th>\n" +
      "                        <td><input name=\"DMA\"><input name=\"DMA\"><input name=\"DMA\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                    </tr>\n" +
      "                    <tr>\n" +
      "                        <th>EMA</th>\n" +
      "                        <td><input name=\"EMA\"><input name=\"EMA\"><input name=\"EMA\"><input name=\"EMA\"><br><input\n" +
      "                                name=\"EMA\"><input name=\"EMA\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                        <th>TRIX</th>\n" +
      "                        <td><input name=\"TRIX\"><input name=\"TRIX\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                    </tr>\n" +
      "                    <tr>\n" +
      "                        <th>VOLUME</th>\n" +
      "                        <td><input name=\"VOLUME\"><input name=\"VOLUME\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                        <th>BRAR</th>\n" +
      "                        <td><input name=\"BRAR\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                    </tr>\n" +
      "                    <tr>\n" +
      "                        <th>MACD</th>\n" +
      "                        <td><input name=\"MACD\"><input name=\"MACD\"><input name=\"MACD\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                        <th>VR</th>\n" +
      "                        <td><input name=\"VR\"><input name=\"VR\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                    </tr>\n" +
      "                    <tr>\n" +
      "                        <th>KDJ</th>\n" +
      "                        <td><input name=\"KDJ\"><input name=\"KDJ\"><input name=\"KDJ\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                        <th>EMV</th>\n" +
      "                        <td><input name=\"EMV\"><input name=\"EMV\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                    </tr>\n" +
      "                    <tr>\n" +
      "                        <th>StochRSI</th>\n" +
      "                        <td><input name=\"StochRSI\"><input name=\"StochRSI\"><input name=\"StochRSI\"><input name=\"StochRSI\">\n" +
      "                        </td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                        <th>WR</th>\n" +
      "                        <td><input name=\"WR\"><input name=\"WR\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                    </tr>\n" +
      "                    <tr>\n" +
      "                        <th>RSI</th>\n" +
      "                        <td><input name=\"RSI\"><input name=\"RSI\"><input name=\"RSI\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                        <th>ROC</th>\n" +
      "                        <td><input name=\"ROC\"><input name=\"ROC\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                    </tr>\n" +
      "                    <tr>\n" +
      "                        <th>DMI</th>\n" +
      "                        <td><input name=\"DMI\"><input name=\"DMI\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                        <th>MTM</th>\n" +
      "                        <td><input name=\"MTM\"><input name=\"MTM\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                    </tr>\n" +
      "                    <tr>\n" +
      "                        <th>OBV</th>\n" +
      "                        <td><input name=\"OBV\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                        <th>PSY</th>\n" +
      "                        <td><input name=\"PSY\"><input name=\"PSY\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                    </tr>\n" +
      "                    <tr>\n" +
      "                        <th>BOLL</th>\n" +
      "                        <td><input name=\"BOLL\"></td>\n" +
      "                        <td>\n" +
      "                            <button class=\"chart_str_default\">默认值</button>\n" +
      "                        </td>\n" +
      "                    </tr>\n" +
      "                    </tbody>\n" +
      "                </table>\n" +
      "                <div id=\"close_settings\"><a class=\"chart_str_close\">关闭</a></div>\n" +
      "            </div>\n" +
      "            <!-- Loading -->\n" +
      "            <div id=\"chart_loading\" class=\"chart_str_loading\">正在读取数据...</div>\n" +
      "        </div>\n" +
      "        <div style=\"display: none\" id=\"chart_language_switch_tmp\">\n" +
      "            <span name=\"chart_str_period\" zh_tw=\"週期\" zh_cn=\"周期\" en_us=\"TIME\"></span>\n" +
      "            <span name=\"chart_str_period_line\" zh_tw=\"分時\" zh_cn=\"分时\" en_us=\"Line\"></span>\n" +
      "            <span name=\"chart_str_period_1m\" zh_tw=\"1分钟\" zh_cn=\"1分钟\" en_us=\"1m\"></span>\n" +
      "            <span name=\"chart_str_period_3m\" zh_tw=\"3分钟\" zh_cn=\"3分钟\" en_us=\"3m\"></span>\n" +
      "            <span name=\"chart_str_period_5m\" zh_tw=\"5分钟\" zh_cn=\"5分钟\" en_us=\"5m\"></span>\n" +
      "            <span name=\"chart_str_period_15m\" zh_tw=\"15分钟\" zh_cn=\"15分钟\" en_us=\"15m\"></span>\n" +
      "            <span name=\"chart_str_period_30m\" zh_tw=\"30分钟\" zh_cn=\"30分钟\" en_us=\"30m\"></span>\n" +
      "            <span name=\"chart_str_period_1h\" zh_tw=\"1小時\" zh_cn=\"1小时\" en_us=\"1h\"></span>\n" +
      "            <span name=\"chart_str_period_2h\" zh_tw=\"2小時\" zh_cn=\"2小时\" en_us=\"2h\"></span>\n" +
      "            <span name=\"chart_str_period_4h\" zh_tw=\"4小時\" zh_cn=\"4小时\" en_us=\"4h\"></span>\n" +
      "            <span name=\"chart_str_period_6h\" zh_tw=\"6小時\" zh_cn=\"6小时\" en_us=\"6h\"></span>\n" +
      "            <span name=\"chart_str_period_12h\" zh_tw=\"12小時\" zh_cn=\"12小时\" en_us=\"12h\"></span>\n" +
      "            <span name=\"chart_str_period_1d\" zh_tw=\"日線\" zh_cn=\"日线\" en_us=\"1d\"></span>\n" +
      "            <span name=\"chart_str_period_3d\" zh_tw=\"3日\" zh_cn=\"3日\" en_us=\"3d\"></span>\n" +
      "            <span name=\"chart_str_period_1w\" zh_tw=\"周線\" zh_cn=\"周线\" en_us=\"1w\"></span>\n" +
      "\n" +
      "            <span name=\"chart_str_settings\" zh_tw=\"更多\" zh_cn=\"更多\" en_us=\"MORE\"></span>\n" +
      "            <span name=\"chart_setting_main_indicator\" zh_tw=\"均線設置\" zh_cn=\"均线设置\" en_us=\"Main Indicator\"></span>\n" +
      "            <span name=\"chart_setting_main_indicator_none\" zh_tw=\"關閉均線\" zh_cn=\"关闭均线\" en_us=\"None\"></span>\n" +
      "            <span name=\"chart_setting_indicator_parameters\" zh_tw=\"指標參數設置\" zh_cn=\"指标参数设置\"\n" +
      "                  en_us=\"Indicator Parameters\"></span>\n" +
      "\n" +
      "            <span name=\"chart_str_chart_style\" zh_tw=\"主圖樣式\" zh_cn=\"主图样式\" en_us=\"Chart Style\"></span>\n" +
      "            <span name=\"chart_str_main_indicator\" zh_tw=\"主指標\" zh_cn=\"主指标\" en_us=\"Main Indicator\"></span>\n" +
      "            <span name=\"chart_str_indicator\" zh_tw=\"技術指標\" zh_cn=\"技术指标\" en_us=\"Indicator\"></span>\n" +
      "            <span name=\"chart_str_indicator_cap\" zh_tw=\"技術指標\" zh_cn=\"技术指标\" en_us=\"INDICATOR\"></span>\n" +
      "            <span name=\"chart_str_tools\" zh_tw=\"畫線工具\" zh_cn=\"画线工具\" en_us=\"Tools\"></span>\n" +
      "            <span name=\"chart_str_tools_cap\" zh_tw=\"畫線工具\" zh_cn=\"画线工具\" en_us=\"TOOLS\"></span>\n" +
      "            <span name=\"chart_str_theme\" zh_tw=\"主題選擇\" zh_cn=\"主题选择\" en_us=\"Theme\"></span>\n" +
      "            <span name=\"chart_str_theme_cap\" zh_tw=\"主題選擇\" zh_cn=\"主题选择\" en_us=\"THEME\"></span>\n" +
      "\n" +
      "            <span name=\"chart_language_setting\" zh_tw=\"語言(LANG)\" zh_cn=\"语言(LANG)\" en_us=\"LANGUAGE\"></span>\n" +
      "            <span name=\"chart_exchanges_setting\" zh_tw=\"更多市場\" zh_cn=\"更多市场\" en_us=\"MORE MARKETS\"></span>\n" +
      "            <span name=\"chart_othercoin_setting\" zh_tw=\"其它市場\" zh_cn=\"其它市场\" en_us=\"OTHER MARKETS\"></span>\n" +
      "\n" +
      "\n" +
      "            <span name=\"chart_str_none\" zh_tw=\"關閉\" zh_cn=\"关闭\" en_us=\"None\"></span>\n" +
      "\n" +
      "            <span name=\"chart_str_theme_dark\" zh_tw=\"深色主題\" zh_cn=\"深色主题\" en_us=\"Dark\"></span>\n" +
      "            <span name=\"chart_str_theme_light\" zh_tw=\"淺色主題\" zh_cn=\"浅色主题\" en_us=\"Light\"></span>\n" +
      "\n" +
      "            <span name=\"chart_str_on\" zh_tw=\"開啟\" zh_cn=\"开启\" en_us=\"On\"></span>\n" +
      "            <span name=\"chart_str_off\" zh_tw=\"關閉\" zh_cn=\"关闭\" en_us=\"Off\"></span>\n" +
      "\n" +
      "            <span name=\"chart_str_close\" zh_tw=\"關閉\" zh_cn=\"关闭\" en_us=\"CLOSE\"></span>\n" +
      "            <span name=\"chart_str_default\" zh_tw=\"默認值\" zh_cn=\"默认值\" en_us=\"default\"></span>\n" +
      "\n" +
      "            <span name=\"chart_str_loading\" zh_tw=\"正在讀取數據...\" zh_cn=\"正在读取数据...\" en_us=\"Loading...\"></span>\n" +
      "\n" +
      "            <span name=\"chart_str_indicator_parameters\" zh_tw=\"指標參數設置\" zh_cn=\"指标参数设置\"\n" +
      "                  en_us=\"Indicator Parameters\"></span>\n" +
      "\n" +
      "            <span name=\"chart_str_cursor\" zh_tw=\"光標\" zh_cn=\"光标\" en_us=\"Cursor\"></span>\n" +
      "            <span name=\"chart_str_cross_cursor\" zh_tw=\"十字光標\" zh_cn=\"十字光标\" en_us=\"Cross Cursor\"></span>\n" +
      "            <span name=\"chart_str_seg_line\" zh_tw=\"線段\" zh_cn=\"线段\" en_us=\"Trend Line\"></span>\n" +
      "            <span name=\"chart_str_straight_line\" zh_tw=\"直線\" zh_cn=\"直线\" en_us=\"Extended\"></span>\n" +
      "            <span name=\"chart_str_ray_line\" zh_tw=\"射線\" zh_cn=\"射线\" en_us=\"Ray\"></span>\n" +
      "            <span name=\"chart_str_arrow_line\" zh_tw=\"箭頭\" zh_cn=\"箭头\" en_us=\"Arrow\"></span>\n" +
      "            <span name=\"chart_str_horz_seg_line\" zh_tw=\"水平線段\" zh_cn=\"水平线段\" en_us=\"Horizontal Line\"></span>\n" +
      "            <span name=\"chart_str_horz_straight_line\" zh_tw=\"水平直線\" zh_cn=\"水平直线\" en_us=\"Horizontal Extended\"></span>\n" +
      "            <span name=\"chart_str_horz_ray_line\" zh_tw=\"水平射線\" zh_cn=\"水平射线\" en_us=\"Horizontal Ray\"></span>\n" +
      "            <span name=\"chart_str_vert_straight_line\" zh_tw=\"垂直直線\" zh_cn=\"垂直直线\" en_us=\"Vertical Extended\"></span>\n" +
      "            <span name=\"chart_str_price_line\" zh_tw=\"價格線\" zh_cn=\"价格线\" en_us=\"Price Line\"></span>\n" +
      "            <span name=\"chart_str_tri_parallel_line\" zh_tw=\"價格通道線\" zh_cn=\"价格通道线\" en_us=\"Parallel Channel\"></span>\n" +
      "            <span name=\"chart_str_bi_parallel_line\" zh_tw=\"平行直線\" zh_cn=\"平行直线\" en_us=\"Parallel Lines\"></span>\n" +
      "            <span name=\"chart_str_bi_parallel_ray\" zh_tw=\"平行射線\" zh_cn=\"平行射线\" en_us=\"Parallel Rays\"></span>\n" +
      "            <span name=\"chart_str_fib_retrace\" zh_tw=\"斐波納契回調\" zh_cn=\"斐波纳契回调\" en_us=\"Fibonacci Retracements\"></span>\n" +
      "            <span name=\"chart_str_fib_fans\" zh_tw=\"斐波納契扇形\" zh_cn=\"斐波纳契扇形\" en_us=\"Fibonacci Fans\"></span>\n" +
      "\n" +
      "            <span name=\"chart_str_updated\" zh_tw=\"更新於\" zh_cn=\"更新于\" en_us=\"Updated\"></span>\n" +
      "            <span name=\"chart_str_ago\" zh_tw=\"前\" zh_cn=\"前\" en_us=\"ago\"></span>\n" +
      "</div>\n";
  
  function draw(instance) {
      KlineIns = instance;
      KlineTradeIns = new KlineTrade();
      var template = $.parseHTML(template_str);
      for (var k in KlineIns.ranges) {
          var res = $(template).find('[name="' + KlineIns.ranges[k] + '"]');
          res.each(function (i, e) {
              $(e).attr("style", "display:inline-block");
          });
      }
      $(KlineIns.element).html(template);
      var refresh_handler = setInterval(refresh_function, KlineIns.intervalTime);
      if (KlineIns.type == "socket") {
          socketConnect();
      }
      window._setMarketFrom = function (content) {
          Template.displayVolume = false;
          refreshTemplate();
          readCookie();
          ChartManager.getInstance().getChart().setSymbol(content);
      };
      window._set_current_language = function (content) {
          chart_switch_language(content);
      };
      window._set_current_depth = function (content) {
          ChartManager.getInstance().getChart().updateDepth(content);
      };
      window._set_current_url = function (content) {
          KlineIns.url = content;
      };
      window._set_current_contract_unit = function (str) {
          ChartManager.getInstance().getChart().setCurrentContractUnit(str);
      };
      window._set_money_type = function (str) {
          ChartManager.getInstance().getChart().setCurrentMoneyType(str);
      };
      window._set_usd_cny_rate = function (rate) {
          ChartManager.getInstance().getChart()._usd_cny_rate = rate;
      };
      window._setCaptureMouseWheelDirectly = function (b) {
          ChartManager.getInstance().setCaptureMouseWheelDirectly(b);
      };
      window._current_future_change = new MEvent();
      window._current_theme_change = new MEvent();
      KLineMouseEvent();
      ChartManager.getInstance().bindCanvas("main", document.getElementById("chart_mainCanvas"));
      ChartManager.getInstance().bindCanvas("overlay", document.getElementById("chart_overlayCanvas"));
      refreshTemplate();
      on_size(KlineIns.width, KlineIns.height);
      readCookie();
  
      KlineIns.setTheme(KlineIns.theme);
      KlineIns.setLanguage(KlineIns.language);
  
      $(KlineIns.element).css({visibility: "visible"});
  }
  
  try {
      module.exports = exports = Kline;
  } catch (e) {
  }
  